|
Boost : |
From: John Torjo (john_at_[hidden])
Date: 2003-04-24 08:06:51
Since there seems to be some confusion about this, I will post the message I
sent to Reece in the first place:
Hi Reece,
I've looked at col_io library -
http://groups.yahoo.com/group/boost/files/col_io/, since I've written a
(note: at that time, I thought Reece developed it)
library that allows thread-safe logging
(http://groups.yahoo.com/group/boost/files/thread_safe_log.zip).
I wanted to see if they "get along" and unfortunately the answer is
something like "sometimes yes, sometimes no".
The point is that I really like the col_io library.
I would have a few remarks if it's ok with you:
1. at this time, you donot care about formatting.
So, something like this will most likely not work as expected:
std::ofstream out("out.txt");
marg_stream m(out);
locale l( "german");
out.imbue( l);
m << 5.23 << std::endl;
2. binding marg_stream to a std::ostream& couples them too much IMHO
(that is, the marg_stream variable is coupled to the other stream).
This actually came to me when I wanted to use col_io together with my
thread_safe_log library.
The point I'm trying to make (the way I see it) is that I'm not sure you
should keep a reference to a std::ostream inside marg_ostream. First of all,
because the reference might become invalid if the underlying stream is
destroyed (which actually happened in my case, since I use temporaries
heavily). Second, the underlying stream and the marg_ostream variable might
go out-of-sync (formatting information, state, etc.) which could cause
unexpected behavior - see the example above.
My suggestion (I might be totally wrong here) is to decouple the
marg_ostream from the underlying stream. This could happen like this:
(I have attached a short example; note: now, it's at the bottom)
- have an 'indenter' class (your marg_ostream class), which holds the indent
information (for example, how many tabs to prepend to each message)
- when you want to write indented information, just use member functions:
.indent(out) or .indent_asis(out)
(out being the underlying stream, to which you want to output indented
information).
This will create a temporary. You can pass this to a function that writes
to a
std::ostream&.
(see indent_ostream_wrapper; indent_ostream_wrapper should never be used
directly!).
You can also specifically create an indent_ostream, which uses the indent
information and writes to an underlying stream.
Both indent_ostream and indent_ostream_wrapper are meant to be used as RAII
(resource acquisition is initialization) objects - they are destroyed as
soon as they are not needed anymore.
Please let me know what you think.
Best,
John
-- John Torjo -- "Practical C++" column writer for builder.com.com Freelancer, C++ consultant mailto:john_at_[hidden] ----------------- #include <streambuf> #include <iostream> #include <sstream> #include <memory> class indenter_streambuf : public std::streambuf { protected: virtual int sync() { // prepend the indent! // // note that basically, we should prepend the indent for each line! std::string str = m_strIndent + m_pBuf->str(); // ... is there anything to write? if ( str.size() > m_strIndent.size()) { m_pDest->sputn( str.c_str(), str.length()); m_pBuf = std::auto_ptr< std::ostringstream>( new std::ostringstream); } return 0; } virtual std::streambuf *setbuf( char * buffer, std::streamsize n) { this->setp( NULL, NULL); return this; } virtual int_type overflow(int_type nChar = traits_type::eof()) { if ( traits_type::not_eof( nChar)) *m_pBuf << ( char_type)nChar; return traits_type::not_eof( nChar); } virtual std::streamsize xsputn(const char_type *S, std::streamsize N) { m_pBuf->write( S, N); return N; } public: indenter_streambuf( std::streambuf * pDest, const std::string & strIndent) : m_pDest( pDest), m_pBuf( new std::ostringstream), m_strIndent( strIndent) {} indenter_streambuf( const indenter_streambuf &from) : m_pDest( from.m_pDest), m_pBuf( new std::ostringstream), m_strIndent( from.m_strIndent) {} ~indenter_streambuf() { sync(); } private: std::auto_ptr< std::ostringstream> m_pBuf; std::streambuf * m_pDest; std::string m_strIndent; }; // forward declaration struct indenter; struct indent_ostream : public std::ostream { typedef std::ostream base_class; inline indent_ostream( const indenter &i, std::ostream & out); inline indent_ostream( const indent_ostream& from); private: std::ostream & m_underlyingOut; indenter_streambuf m_buf; }; // helper - allow passing an indented ostream to a class // (automatic conversion to std::ostream&) struct indent_ostream_wrapper { inline indent_ostream_wrapper( const indenter & i, std::ostream &out); operator std::ostream&() { return m_out; } private: mutable indent_ostream m_out; }; // keeps information about how indenting of a output stream // should occur struct indenter { indenter( int n = 0) : m_n( n) {} int get_indent() const { return m_n; } indenter& operator++() { ++m_n; return *this; } indenter& operator--() { --m_n; return *this; } indenter operator++(int) { indenter tmp( *this); ++(*this); return tmp; } // uses this indenter, as is inline indent_ostream_wrapper indent_asis( std::ostream & out) const; // indents one more time than the current indent inline indent_ostream_wrapper indent( std::ostream & out) const; std::string get_indent_string() const { std::string str; for ( int idx = 0; idx < m_n; ++idx) str += '\t'; return str; } private: int m_n; }; indenter operator+( const indenter& i, int add) { indenter tmp( i.get_indent() + add ); return tmp; } inline indent_ostream_wrapper::indent_ostream_wrapper( const indenter & i, std::ostream &out) : m_out( i, out) {} inline indent_ostream::indent_ostream( const indenter &i, std::ostream & out) : m_buf( out.rdbuf(), i.get_indent_string() ), m_underlyingOut( out), base_class( NULL) { this->init( &m_buf); m_buf.pubsetbuf( NULL, 0); this->copyfmt( m_underlyingOut); } inline indent_ostream::indent_ostream( const indent_ostream& from) : m_buf( from.m_buf), m_underlyingOut( (std::ostream&)( from.m_underlyingOut) ), base_class( NULL) { this->init( &m_buf); m_buf.pubsetbuf( NULL, 0); this->copyfmt( m_underlyingOut); } inline indent_ostream_wrapper indenter::indent_asis( std::ostream & out) const { return indent_ostream_wrapper( *this, out); } // indents one more time than the current indent inline indent_ostream_wrapper indenter::indent( std::ostream & out) const { return indent_ostream_wrapper( *this + 1, out); } ////////////////////////////////////////////// void use_indent_as_ostream( int i, std::ostream & out) { out << "indented message " << i << std::endl; } // workaround for bullshit VC void use_indent_as_ostream( int i, const indent_ostream_wrapper & out) { use_indent_as_ostream( i, (std::ostream&)out ); } void use_indent_recursively( const indenter & i, int val) { indent_ostream out( i, std::cout); out << "start of recursive call - " << val << std::endl; if ( val < 4) use_indent_recursively( i + 1, val + 1); out << "end of recursive call - " << val << std::endl; } int main(int argc, char* argv[]) { indenter i; // here's how it can be used recursively use_indent_recursively( i, 0); std::cout << "\n\n" << std::endl; //////////////////////////////////////////////////// // no indent whatsoever. use_indent_as_ostream( 0, i.indent_asis( std::cout)); // indent one tab use _indent_as_ostream( 1, i.indent( std::cout)); ++i; // indent two tabs use_indent_as_ostream( 2, i.indent( std::cout)); std::cin.get(); return 0; }
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk