In our project we use the c++ stream operator (<<) in our object model to print out an easy readible format of the data. A simplified example is this:
The simplest solution is to slip a filtering streambuf between the
ostream
and the actual streambuf. Something like:
class IndentingOStreambuf : public std::streambuf
{
std::streambuf* myDest;
bool myIsAtStartOfLine;
std::string myIndent;
std::ostream* myOwner;
protected:
virtual int overflow( int ch )
{
if ( myIsAtStartOfLine && ch != '\n' ) {
myDest->sputn( myIndent.data(), myIndent.size() );
}
myIsAtStartOfLine = ch == '\n';
return myDest->sputc( ch );
}
public:
explicit IndentingOStreambuf(
std::streambuf* dest, int indent = 4 )
: myDest( dest )
, myIsAtStartOfLine( true )
, myIndent( indent, ' ' )
, myOwner( NULL )
{
}
explicit IndentingOStreambuf(
std::ostream& dest, int indent = 4 )
: myDest( dest.rdbuf() )
, myIsAtStartOfLine( true )
, myIndent( indent, ' ' )
, myOwner( &dest )
{
myOwner->rdbuf( this );
}
virtual ~IndentingOStreambuf()
{
if ( myOwner != NULL ) {
myOwner->rdbuf( myDest );
}
}
};
To insert, just create an instance of the streambuf:
IndentingOStreambuf indent( std::cout );
// Indented output...
When indent
goes out of scope, everything returns to normal.
(For logging, I have one that is a bit more complex: the
LoggingOStreambuf
takes __FILE__
and __LINE__
as arguments, sets
myIndent
to a formatted string with these arguments, plus a time
stamp, resets it to an indentation string after each output, collects
all of the output in an std::ostringstream
, and outputs it atomically
to myDest
in the destructor.)