Boost logo

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