I want a ostream with a prefix at the beginning of every line redirected on cout;
I try this:
#include <iostream>
#include <thread>
class parallel_cout : public std::ostream
{
public:
parallel_cout(std::ostream& o):out(o){}
template <typename T>
std::ostream& operator<< (const T& val)
{
out << "prefix " << val;
return *this;
}
std::ostream& out;
};
int main()
{
parallel_cout pc(std::cout);
pc<<"a\nb"<<"c\n";
}
but i have in output
prefix a
b
without c. why this?
The way you modify the behavior of std::ostream
is not by overloading any of the output operators! Instead, you derive a class from std::streambuf
and override the virtual
functions overflow()
and sync()
. In you case you'd probably create a filtering stream buffer, i.e., you'd take another std::streambuf
as argument and write a somehow modified stream of characters to this stream buffer.
Here is a quick example:
#include <iostream>
class prefixbuf
: public std::streambuf
{
std::string prefix;
std::streambuf* sbuf;
bool need_prefix;
int sync() {
return this->sbuf->pubsync();
}
int overflow(int c) {
if (c != std::char_traits<char>::eof()) {
if (this->need_prefix
&& !this->prefix.empty()
&& this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) {
return std::char_traits<char>::eof();
}
this->need_prefix = c == '\n';
}
return this->sbuf->sputc(c);
}
public:
prefixbuf(std::string const& prefix, std::streambuf* sbuf)
: prefix(prefix)
, sbuf(sbuf)
, need_prefix(true) {
}
};
class oprefixstream
: private virtual prefixbuf
, public std::ostream
{
public:
oprefixstream(std::string const& prefix, std::ostream& out)
: prefixbuf(prefix, out.rdbuf())
, std::ios(static_cast<std::streambuf*>(this))
, std::ostream(static_cast<std::streambuf*>(this)) {
}
};
int main()
{
oprefixstream out("prefix: ", std::cout);
out << "hello\n"
<< "world\n";
}
Stream buffers conceptually keep an internal buffer which is, however, not set up in the example above. Every time there is no space for a character to be written to the output buffer, the virtual
function overflow()
is called with a character (it may also be called with the special value std::char_traits<char>::eof()
which is typically used to flush the buffer). Since there is no buffer, overflow()
will be called for every character. All this function does is to see if it needs to write a prefix and, if so, writes the prefix
. In case a newline '\n'
is written, the function remembers that it needs to write the prefix
if another character is written. It then just forwards writing of the character to the underlying stream buffer.
The virtual
function sync()
is used to synchronize the stream with its external representation. For a stream buffer keeping a buffer it makes sure that any buffer is written. Since the prefixbuf
doesn't really keep a buffer, all it needs to is to delegate the sync()
request to the underlying stream buffer by calling pubsync()
.
As Dietmar said, what you want is your own streambuf, something on this general order:
#include <streambuf>
#include <iostream>
class prefixer: public std::streambuf {
public:
prefixer(std::streambuf* s): sbuf(s) {}
~prefixer() { overflow('\n'); }
private:
typedef std::basic_string<char_type> string;
int_type overflow(int_type c) {
if (traits_type::eq_int_type(traits_type::eof(), c))
return traits_type::not_eof(c);
switch (c) {
case '\n':
case '\r': {
prefix = "[FIX]";
buffer += c;
if (buffer.size() > 1)
sbuf->sputn(prefix.c_str(), prefix.size());
int_type rc = sbuf->sputn(buffer.c_str(), buffer.size());
buffer.clear();
return rc;
}
default:
buffer += c;
return c;
}
}
std::string prefix;
std::streambuf* sbuf;
string buffer;
};
To use this, you create an instance of it from some other streambuf (that defines where it's going to write to) and then (usually) create an ostream using this streambuf. Then you can write to that ostream, and your prefix gets written to each line of output. For example:
int main() {
prefixer buf(std::cout.rdbuf());
std::ostream out(&buf);
out << "Test\n";
}
Alternatively you could write a function though the syntax changes.
template<typename T>
void print(T content)
{
cout<<whatever your prefix is;
cout<<content;
}
Your output operator is returning a reference to the base class (std::ostream
), which means that the next call to operator <<
will not use your implementation.
Changing your output operator to this should fix your problem:
template <typename T>
parallel_cout& operator<< (const T& val)
{
out << "prefix " << val;
return *this;
}