all. I am using a slightly-modified version of the class defined at https://stackoverflow.com/a/1761027/2909854 which allows me to combine streams so that anything sent to the combined stream goes to all of the linked streams. This code works well for except for two issues:
deprecated-declarations
warnings (that answer is from 2009).CombinedStream
object, when I use it, I get a segmentation fault.Code:
#include <iostream>
// Needed for ComposeStream
#include <algorithm>
#include <vector>
#include <fstream>
#include <functional>
// A class that allows us to combine streams so that anything sent to the combined stream goes to all of them
// Modified from https://stackoverflow.com/a/1761027/2909854
class CombinedStream: public std::ostream
{
struct ComposeBuffer: public std::streambuf
{
void addBuffer(std::streambuf* buf)
{
bufs.push_back(buf);
}
virtual int overflow(int c)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
std::for_each(bufs.begin(),bufs.end(),std::bind2nd(std::mem_fun(&std::streambuf::sputc),c));
#pragma GCC diagnostic pop
return c;
}
private:
std::vector<std::streambuf*> bufs;
};
ComposeBuffer myBuffer;
public:
CombinedStream(): std::ostream(NULL)
{
std::ostream::rdbuf(&myBuffer);
}
void linkStream(std::ostream& out)
{
out.flush();
myBuffer.addBuffer(out.rdbuf());
}
};
class Parent
{
public:
Parent();
protected:
std::fstream m_outputFile;
CombinedStream m_outputFileAndCout;
};
class Child: protected Parent
{
public:
Child();
};
Parent::Parent()
{
std::string filePath = "test_file.test";
std::cout << "Opening " << filePath << "\n\n";
std::fstream m_outputFile(filePath, std::ios::app);
// Create a stream that combines cout and the process log
m_outputFileAndCout.linkStream(std::cout);
m_outputFileAndCout.linkStream(m_outputFile);
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
m_outputFileAndCout << "I'm working!" << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
}
Child::Child()
{
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
m_outputFileAndCout << "I'm crashing!" << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
}
int main()
{
Child object;
return 0;
}
Compilation:
g++-13 inherited_stream.cpp -o inherited_stream.exe -std=c++20 -g
Questions:
deprecated-declarations
warnings is causing this issue? If so, can you help me update the std::for_each...
line to resolve these?Here is a gdb
backtrace:
(gdb) run
Starting program: /home/trackingtech/gateway/experiments/inherited_stream.exe
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Opening test_file.test
inherited_stream.cpp:72
I'm working!
inherited_stream.cpp:74
inherited_stream.cpp:79
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x0000aaaaaaaa46a4 in std::mem_fun1_t<int, std::basic_streambuf<char, std::char_traits<char> >, char>::operator() (this=0xffffffffed60, __p=0xffffffffecd8, __x=73 'I') at /usr/include/c++/13/bits/stl_function.h:1309
#2 0x0000aaaaaaaa416c in std::binder2nd<std::mem_fun1_t<int, std::basic_streambuf<char, std::char_traits<char> >, char> >::operator() (this=0xffffffffed60, __x=@0xaaaaaaacc8d8: 0xffffffffecd8)
at /usr/include/c++/13/backward/binders.h:165
#3 0x0000aaaaaaaa38c8 in std::for_each<__gnu_cxx::__normal_iterator<std::basic_streambuf<char, std::char_traits<char> >**, std::vector<std::basic_streambuf<char, std::char_traits<char> >*, std::allocator<std::basic_streambuf<char, std::char_traits<char> >*> > >, std::binder2nd<std::mem_fun1_t<int, std::basic_streambuf<char, std::char_traits<char> >, char> > > (__first=0xffffffffecd8, __last=0x0, __f=...) at /usr/include/c++/13/bits/stl_algo.h:3833
#4 0x0000aaaaaaaa2cdc in CombinedStream::ComposeBuffer::overflow (this=0xfffffffff158, c=73) at inherited_stream.cpp:23
#5 0x0000fffff7e9f5b4 in std::basic_streambuf<char, std::char_traits<char> >::xsputn(char const*, long) () from /lib/aarch64-linux-gnu/libstdc++.so.6
#6 0x0000fffff7e9159c in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) ()
from /lib/aarch64-linux-gnu/libstdc++.so.6
#7 0x0000fffff7e91950 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) () from /lib/aarch64-linux-gnu/libstdc++.so.6
#8 0x0000aaaaaaaa29b8 in Child::Child (this=0xffffffffef38) at inherited_stream.cpp:80
#9 0x0000aaaaaaaa2a50 in main () at inherited_stream.cpp:86
(gdb)
In the Parent
constructor you define a brand new and local variable m_outputFile
and use it instead of the member variable of the same name.
That means the pointer to the buffer you add to the combined stream will become invalid after the Parent
constructor function ends, and the local m_outputFile
object is destructed.
Since you hard-code the filename you should use a constructor initializer list to initialize the member object:
Parent::Parent()
: m_outputFile("test_file.test", std::ios::app) // Initialize the member variable
{
// Create a stream that combines cout and the process log
m_outputFileAndCout.linkStream(std::cout);
m_outputFileAndCout.linkStream(m_outputFile);
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
m_outputFileAndCout << "I'm working!" << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
}
To solve the warning, instead of temporarily disabling it you can use a lambda instead of std::bind2nd
and std::mem_fn
:
std::for_each(bufs.begin(), bufs.end(),
[c](std::streambuf* buffer)
{
buffer->sputc(c);
});
Or perhaps better yet, use a range for
loop:
for (auto buffer : bufs)
buffer->sputc(c);