I'm trying to write a scoped object to redirect output from std::cout and call a function when it's underlying buffer is flushed.
My implementation borrows heavily from the following SO answer:
c++ execute function any time a stream is written to
I have it partially working, but the callback function is only called when I explicity call flush on std::cout. However, I want it to call the callback function whenever anything is written to the stream.
NOTE: I am compiling aginst MSVC++11.
struct stream_redirect
{
stream_redirect(std::ostream& stream, std::streambuf* rdbuf) :
stream(stream),
rdbuf_old(stream.rdbuf())
{
stream.set_rdbuf(rdbuf);
}
~stream_redirect()
{
stream.set_rdbuf(rdbuf_old);
}
private:
stream_redirect(const stream_redirect&) = delete;
stream_redirect& operator = (const stream_redirect&) = delete;
std::ostream& stream;
std::streambuf* rdbuf_old;
};
struct functionbuf : public std::streambuf
{
typedef std::function<void(std::string)> function_type;
functionbuf(const function_type& function)
: function(function)
{
setp(buffer, buffer + sizeof(buffer) - 1);
}
private:
char buffer[1024];
function_type function;
virtual int_type overflow(int_type c) override
{
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
*this->pptr() = traits_type::to_char_type(c);
pbump(1);
}
return sync() ? traits_type::not_eof(c) : traits_type::eof();
}
virtual int_type sync() override
{
if (pbase() != pptr())
{
function(std::string(pbase(), pptr()));
setp(pbase(), epptr());
}
return 0;
}
};
struct ofunctionstream :
private virtual functionbuf,
public std::ostream
{
ofunctionstream(const function_type& function) :
functionbuf(function),
std::ostream(static_cast<std::streambuf*>(this))
{
setf(std::ios_base::unitbuf);
}
};
Now a usage example:
void callback(std::string string)
{
printf("callback(%s)\n", string.c_str());
}
int main()
{
ofunctionstream fs(&callback);
stream_redirect sr(std::cout, fs.rdbuf());
printf("sending string to cout...");
std::cout << "hello!";
printf("string sent to cout");
//this is necessary to
printf("flushing cout...");
std::cout.flush();
printf("cout flushed");
}
I get the following output:
sending string to cout...
string sent to cout
flushing cout...
callback(hello!)
cout flushed
Again, I want that callback function to be called as soon as std::cout << "hello!"; is called. I assumed this would happen since I am calling setf(std::ios_base::unitbuf) (http://en.cppreference.com/w/cpp/io/manip/unitbuf) on the ofunctionstream object in it's constructor.
Any help is greatly appreciated!
If you examine how the callback that you're using works, it works by subclassing
std::streambufand by overridingoverflow(). That's important to note.Quoting the relevant parts of the C++ standard library:
std::ostream, a.k.a. formatted output, writes to the stream buffer using sputc(). So, the only timeoverflow()gets invoked is when the output buffer is exhausted. Or explicitly viastd::flush.So, your options are, unfortunately, somewhat limited. Either deal with the current behavior, or jury-rig the
std::streambufsubclass to have no write buffer at all, so that every character that ends up getting written viasputc()is going to get punted tooverflow(), and invoke your subclass implementation.Unfortunately, stream operations do not carry out any explicit action after every formatting operation, that can be intercepted by the
std::streambuf. They just write the formatted output, one character at a time, viasputc(), so that the output gets collected in the streambuf's write buffer, which gets flushed out only when it's full, or whenstd::flushgets explicitly used.So, that's how it works.