Reputation: 36679
I would like to write the <<
operators for my classes, which in general contain some structured data. One of the easiest examples is a class containing a list of matrices, e.g.
stuct MyClass
{
std::vector<Matrix> m;
}
assume that the Matrix class can print itself (using multiple lines), and that we don't control this class. Now I would like to get the following output:
A(0)=[a b
c d]
A(1)=[e f
g h]
and that the Matrix
class itself outputs:
[a b
c d]
how can configure/change the stream or do something completely different to make it work, i.e. to make the stream output a given number of spaces after every std::endl
?
Upvotes: 2
Views: 210
Reputation: 154007
This is actually the first real use I made of filtering streambuf's. Given something like:
std::ostream& operator<<( std::ostream& dest,
std::vector<Matrix> const& object );
class MyClass
{
std::vector<Matrix> m;
};
std::ostream& operator<<( std::ostream& dest, MyClass const& object )
{
// ...
{
// Scope to control lifetime of the IndentingStreambuf...
// Could be (probably should be) in a separate function.
IndentingStreambuf indent( dest )
dest << m;
}
// ...
}
Where:
class IndentingStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ostream* myOwner;
bool myIsAtStartOfLine;
protected:
int overflow( int ch )
{
if ( myIsAtStartOfLine && ch != EOF && ch != '\n' ) {
myDest->sputn( " ", 4 );
}
myIsAtStartOfLine = ch == '\n';
return myDest->sputc( ch );
}
public:
IndentingStreambuf( std::streambuf* dest )
: myDest( dest )
, myOwner( NULL )
, myIsAtStartOfLine( true )
{
}
IndentingStreambuf( std::ostream& dest )
: myDest( dest.rdbuf() )
, myOwner( &dest )
, myIsAtStartOfLine( true )
{
myOwner->rdbuf( this );
}
~IndentingStreambuf()
{
if ( myOwner != NULL ) {
myOwner->rdbuf( myDest );
}
}
};
EDIT:
I just read your desired output in a little more detail. The
same basic IndentingStreambuf
works, but
you'll have to adapt it to support an arbitrary indent (as argument to the constructor),
initialize myIsAtStartOfLine
to false
, and
call it after having output A(i) = [
.
And of course, define operator<<
for Matrix
appropriately.
Alternatively, you could just use std::ios_base::xalloc
to
obtain a place where you could tell the operator<<
for
Matrix
about the indent. Something along the lines of:
static long& getMatrixIndent( std::ostream& dest )
{
static int ourIndex = std::ostream::xalloc();
return dest.iword( ourIndex );
}
class indent
{
int myIndent;
public:
indent( int n ) : myIndent( n ) {}
friend std::ostream& operator<<( std::ostream& dest,
indent const& manip )
{
getMatrixIndent( dest ) = myIndent;
}
};
std::ostream& operator<<( std::ostream& dest, Matrix const& object )
{
int indent = getMatrixIndent( dest );
// ...
}
This is considerably less flexible than a filtering streambuf.
In particular, it can't be made to work for types where you
don't control the implementation of operator<<
. But it's
simple, and also useful at times.
(I'd argue, in fact, that anyone using C++ should be familiar
with both techniques. But I'm not aware of any tutorial text
which even mentions them. And in a world where a large number
of C++ users are still writing while ( ! file.eof() )
, it's
probably too much to ask for.)
Upvotes: 1