Listing J
// Solution 4 (final solution)
// note: compiles with gcc 3.2, VC6 and
// Comeau++ (http://www.comeaucomputing.com/tryitout/)
#include "message_handler_log.h"
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <assert.h>
#include <algorithm>
 
 
 
 
// comment this line and uncomment the following one,
// in order to use BOOST
#define USE_WIN32_THREAD_MANAGER
// #define USE_BOOST_THREAD_MANAGER
 
 
 
 
////////////////////////////////////////////////////////////////////
// thread managers
 
 
#ifdef USE_WIN32_THREAD_MANAGER
#include <windows.h>
 
 
// the object to be started - on a given thread
struct win32_thread_obj
{
    virtual ~win32_thread_obj() {}
    virtual void operator()() = 0;
};
 
 
struct win32_thread_manager
{
    typedef win32_thread_obj thread_obj_base;
 
 
    static void sleep( int nMillisecs) { Sleep( nMillisecs); }
 
 
    static void create_thread( win32_thread_obj & obj)
    {
        DWORD dwThreadID;
        CreateThread( 0, 0,
            win32_thread_manager::ThreadProc, &obj, 0, &dwThreadID);
    }
 
 
    // critical section for Win32
    class critical_section
    {
     critical_section & operator = ( const critical_section & Not_Implemented);
     critical_section( const critical_section & From);
    public:
     critical_section() { InitializeCriticalSection( GetCsPtr() ); }
        ~critical_section() { DeleteCriticalSection( GetCsPtr() ); }
     void Lock() { EnterCriticalSection( GetCsPtr()); }
     void Unlock() { LeaveCriticalSection( GetCsPtr()); }
     operator LPCRITICAL_SECTION() const { return GetCsPtr(); }
    private:
     LPCRITICAL_SECTION GetCsPtr() const { return &m_cs; }
    private:
     // the critical section itself
        mutable CRITICAL_SECTION m_cs;
    };
 
 
    // automatic locking/unlocking of a resource
    class auto_lock_unlock
    {
     auto_lock_unlock operator=( auto_lock_unlock & Not_Implemented);
     auto_lock_unlock( const auto_lock_unlock & Not_Implemented);
    public:
     auto_lock_unlock( critical_section & cs) : m_cs( cs) { m_cs.Lock(); }
     ~auto_lock_unlock() { m_cs.Unlock(); }
    private:
     critical_section & m_cs;
    };
 
 
private:
    static DWORD WINAPI ThreadProc( LPVOID lpData)
    {
        win32_thread_obj * pThread = ( win32_thread_obj *)lpData;
        ( *pThread)();
        return 0;
    }
};
 
 
#endif // ifdef USE_WIN32_THREAD_MANAGER
 
 
 
 
#ifdef USE_BOOST_THREAD_MANAGER
 
 
#include "boost/thread/mutex.hpp"
#include "boost/thread/thread.hpp"
#include "boost/function.hpp"
#include "boost/thread/xtime.hpp"
 
 
class boost_thread_manager
{
 
 
public:
    struct thread_obj_base
    {
        virtual void operator()() = 0;
    };
 
 
private:
    struct function_wrapper
    {
        function_wrapper( thread_obj_base & base) : m_base( base) {}
        void operator()() const { m_base(); }
    private:
        mutable thread_obj_base & m_base;
    };
public:
 
 
    static void sleep( int nMillisecs)
    {
       boost::xtime xt;
       boost::xtime_get(&xt, boost::TIME_UTC);
       // Sleep for n Millisecs
       xt.nsec += 1000000 * nMillisecs
           + 100000 /* just in case*/;
       boost::thread::sleep( xt);
    }
 
 
    static void create_thread( thread_obj_base & obj)
    {
        boost::function0< void> f = function_wrapper( obj);
        // creates the thread
        boost::thread t( f);
    }
 
 
    typedef boost::mutex critical_section;
    typedef boost::mutex::scoped_lock auto_lock_unlock;
 
 
};
 
 
#endif // #ifdef USE_BOOST_THREAD_MANAGER
 
 
 
 
// if you want a custom default manager,
// create your custom thread_manager class, like
// the ones shown in win32_thread_manager or
// boost_thread_manager, and
// #define DEFAULT_THREAD_MANAGER <your_custom_class>
//
#if defined( DEFAULT_THREAD_MANAGER)
    // custom thread manager
 
 
#elif defined( USE_WIN32_THREAD_MANAGER)
    #define DEFAULT_THREAD_MANAGER win32_thread_manager
 
 
#elif defined( USE_BOOST_THREAD_MANAGER)
    #define DEFAULT_THREAD_MANAGER boost_thread_manager
 
 
#else
#error "No thread manager. #define either of USE_WIN32_THREAD_MANAGER, USE_BOOST_THREAD_MANAGER, DEFAULT_THREAD_MANAGER"
#endif
 
 
 
 
// END OF thread managers
////////////////////////////////////////////////////////////////////
 
 
 
 
// forward declaration
template< class char_type, class traits_type = std::char_traits< char_type> >
    class basic_thread_safe_log;
 
 
// base class for our internal thread_safe_log object
template< class char_type, class traits_type>
    class basic_internal_thread_safe_log_base
{
    typedef basic_internal_thread_safe_log_base< char_type, traits_type> this_class;
    typedef typename std::basic_ostream< char_type, traits_type> ostream_type;
    friend class basic_thread_safe_log< char_type, traits_type>;
 
 
    // non-copyiable
    basic_internal_thread_safe_log_base( const this_class &);
    this_class & operator=( this_class &);
 
 
public:
    virtual void write_message( const std::basic_string< char_type, traits_type> & str) = 0;
    virtual void copy_state_to( ostream_type & dest) const = 0;
    virtual void copy_state_from( const ostream_type & src) = 0;
protected:
    basic_internal_thread_safe_log_base() {}
}; // class basic_internal_thread_safe_log_base
 
 
 
 
////////////////////////////////////////////////////////////////////
// internal_thread_safe_log for SharedThread
// (multiple logs share the same thread for writing to them)
 
 
 
 
// forward declaration
template<
        class char_type,
        class traits_type = std::char_traits< char_type>,
        class thread_manager = DEFAULT_THREAD_MANAGER >
    class basic_internal_thread_safe_log_sharethread;
 
 
// allows thread-safe writing for multiple logs
template<
        class char_type,
        class traits_type = std::char_traits< char_type>,
        class thread_manager = DEFAULT_THREAD_MANAGER >
    class basic_thread_safe_log_writer_sharethread
{
    typedef basic_thread_safe_log_writer_sharethread< char_type, traits_type> this_class;
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    typedef std::basic_string< char_type, traits_type> string_type;
    friend class basic_internal_thread_safe_log_sharethread< char_type, traits_type, thread_manager>;
 
 
    // copying not allowed
    basic_thread_safe_log_writer_sharethread( const this_class &);
    this_class & operator=( const this_class &);
 
 
 
 
    // forward declaration
    struct thread_info;
    friend struct thread_info;
 
 
    // thread-related definitions
    typedef typename thread_manager::thread_obj_base thread_obj_base;
    typedef typename thread_manager::critical_section critical_section;
    typedef typename thread_manager::auto_lock_unlock auto_lock_unlock;
    // so that from our thread we know the object we're manipulating
    struct thread_info : public thread_obj_base
    {
        thread_info()
            : m_bHasFinished( false),
              m_pThis( NULL)
        {}
 
 
        /* virtual */ void operator()()
        {
            while ( true)
            {
                // ... we might be writing multiple messages at once!
                std::vector< std::string *> astrMsgs;
                ostream_type * pLog = NULL;
                bool bDoFlush = false;
                {
                    auto_lock_unlock locker( m_pThis->m_cs);
                    if ( m_pThis->m_nSumOfPriorities <= 0)
                    {
                        // we don't have any logs yet...
                        thread_manager::sleep( 1);              
                        continue;
                   }
 
 
                    // find a log that has messages to be written to it
                    for ( int idx = 0; idx < m_pThis->m_nSumOfPriorities; ++idx)
                    {
                        LogWrites & writes = *( m_pThis->m_aWritesTo[ m_pThis->m_idxWrite]);
                        if ( writes.m_astr.size() > 0)
                            // we found a log that we should write to
                            break;
                        ++m_pThis->m_idxWrite;
                        m_pThis->m_idxWrite %= m_pThis->m_nSumOfPriorities;
                    }
                    // did we find a log with messages that should be written to it?
                    LogWrites & writes = *( m_pThis->m_aWritesTo[ m_pThis->m_idxWrite]);
                   if ( writes.m_astr.size() > 0)
                    {
                        // we get the string(s) to write to this log
                        pLog = writes.m_pDestLog;
                        // optimization - if too many messages, write
                       // multiple messages at once
                        int nMessages = 1;
                        if ( writes.m_astr.size() > 100)
                            nMessages = writes.m_astr.size() / 10;
                        while ( nMessages > 0)
                        {
                            astrMsgs.push_back( writes.m_astr.front());
                            writes.m_astr.pop();
                            --nMessages;
                        }
                        // we flush only when there are no more messages to write
                        // (flushing could be time-consuming)
                        bDoFlush = ( writes.m_astr.size() == 0);
                    }
                    // ... only when there are no more messages,
                    //    will we ask if we should be destructed
                    else if ( m_pThis->m_bShouldBeDestructed)
                    {
                        // signal to the other thread we've finished
                        m_bHasFinished = true;
                        return;
                    }
                }
                // write the string(s)
                if ( astrMsgs.size() > 0)
                {
                    std::vector< std::string *>::iterator
                       first = astrMsgs.begin(), last = astrMsgs.end();
                    while ( first != last)
                    {
                        std::string *pstr = *first;
                        *pLog << *pstr;
                        delete pstr;
                        ++first;
                    }
                    if ( bDoFlush)
                        pLog->flush();
                }
                else
                    // nothing to write - wait
                    thread_manager::sleep( 1);              
            }
        }
 
 
        this_class * m_pThis;
        volatile bool m_bHasFinished;
    };
 
 
public:
 
 
    basic_thread_safe_log_writer_sharethread()
        : m_bShouldBeDestructed( false)
    {
        m_info.m_pThis = this;
        thread_manager::create_thread( m_info);
 
 
        m_nSumOfPriorities = 0;
        m_idxWrite = 0;
    }
 
 
    ~basic_thread_safe_log_writer_sharethread()
    {
        // signal to the other thread we're about to be
        // destructed
       {
            auto_lock_unlock locker( m_cs);
            m_bShouldBeDestructed = true;
        }
        // wait while the other thread writes all messages
        while ( true)
        {
            auto_lock_unlock locker( m_cs);
            if ( m_info.m_bHasFinished)
                // the other thread has finished
                break;
        }
    }
 
 
private:
    // note: only basic_internal_thread_safe_log can
    // call these functions
 
 
    // adds a message to be written to a given log
    void add_message( const string_type & str, ostream_type & log)
    {
        auto_lock_unlock locker( m_cs);
        ostream_type * pLog = &log;
        m_collLogWrites[ pLog].m_astr.push( new string_type( str));
    }
 
 
    // adds a log we can write to, with a given priority
    void add_log( ostream_type & log, int nPriority)
    {
        // priority should be at least one
        assert( nPriority > 0);
 
 
        auto_lock_unlock locker( m_cs);
        ostream_type * pLog = &log;
        m_collLogWrites[ pLog].m_nLogPriority = nPriority;
        m_collLogWrites[ pLog].m_pDestLog = pLog;
        m_nSumOfPriorities += nPriority;
        m_idxWrite = 0;
 
 
        m_aWritesTo.resize( m_nSumOfPriorities);
        std::fill( m_aWritesTo.begin(), m_aWritesTo.end(), ( LogWrites *)0);
 
 
        typename LogWritesCollection::iterator
            first = m_collLogWrites.begin(), last = m_collLogWrites.end();
        while ( first != last)
        {
            LogWrites & writes = first->second;
            for( int idx = 0; idx < writes.m_nLogPriority; ++idx)
            {
                int idxWrite =
                    (int)((double)( idx * m_nSumOfPriorities) / writes.m_nLogPriority);
                // ... find an empty spot
                while ( m_aWritesTo[ idxWrite] != 0)
                {
                    ++idxWrite;
                    idxWrite = idxWrite % m_nSumOfPriorities;
                }
                m_aWritesTo[ idxWrite] = &writes;
            }
           ++first;
        }
    }
 
 
    critical_section & cs() const { return m_cs; }
private:
    // the critical section used for thread-safe locking
    mutable critical_section m_cs;
 
 
    // needed to create the other thread
    thread_info m_info;
    volatile bool m_bShouldBeDestructed;
 
 
 
 
    typedef std::queue< string_type* > StringsQueue;
 
 
    // forward declaration;
    struct LogWrites;
    friend struct LogWrites;
 
 
    struct LogWrites
    {
        LogWrites()
            : m_nLogPriority( 0), m_pDestLog( NULL) {}
 
 
        // the priority of this log
        int m_nLogPriority;
 
 
        // the strings to write to this log
        StringsQueue m_astr;
 
 
        // the log we should write to
        ostream_type * m_pDestLog;
    };
    // at each step, from which log should we write to?
    std::vector< LogWrites* > m_aWritesTo;
 
 
    // for each log, what should we write to it?
    typedef std::map< ostream_type*, LogWrites> LogWritesCollection;
    LogWritesCollection m_collLogWrites;
 
 
    // the sum of all log' priorities
    int m_nSumOfPriorities;
 
 
    // the index of the current write
    // ( always less than m_nSumOfPriorities)
    int m_idxWrite;
};
 
 
typedef basic_thread_safe_log_writer_sharethread< char> thread_safe_log_writer_sharethread;
typedef basic_thread_safe_log_writer_sharethread< wchar_t> wthread_safe_log_writer_sharethread;
 
 
 
 
 
 
// multiple basic_internal_thread_safe logs share the same thread,
// which writes to them
template< class char_type, class traits_type, class thread_manager>
    class basic_internal_thread_safe_log_sharethread
        : public basic_internal_thread_safe_log_base< char_type, traits_type>
{
    typedef basic_internal_thread_safe_log_sharethread< char_type, traits_type, thread_manager> this_class;
   typedef typename std::basic_ostream< char_type, traits_type> ostream_type;
    typedef class basic_thread_safe_log_writer_sharethread< char_type, traits_type, thread_manager> multiple_log_writer;
 
 
    // non-copyiable
    basic_internal_thread_safe_log_sharethread( const this_class &);
    this_class & operator=( this_class &);
 
 
    // thread-related definitions
    typedef typename thread_manager::auto_lock_unlock auto_lock_unlock;
 
 
public:
    basic_internal_thread_safe_log_sharethread(
            ostream_type & underlyingLog,
            multiple_log_writer & writer, int nPriority)
        : m_underlyingLog( underlyingLog),
          m_writer( writer)
    {
        writer.add_log( m_underlyingLog, nPriority);
    }
    ~basic_internal_thread_safe_log_sharethread()
    {}
 
 
    void write_message( const std::basic_string< char_type, traits_type> & str)
    { m_writer.add_message( str, m_underlyingLog); }
 
 
    void copy_state_to( ostream_type & dest) const
    {
        auto_lock_unlock locker( m_writer.cs());
        dest.copyfmt( m_underlyingLog);
        dest.setstate( m_underlyingLog.rdstate());
    }
    void copy_state_from( const ostream_type & src)
    {
        auto_lock_unlock locker( m_writer.cs());
        m_underlyingLog.copyfmt( src);
        m_underlyingLog.setstate( m_underlyingLog.rdstate());
    }
private:
    ostream_type & m_underlyingLog;
    // IMPORTANT: keep it by reference!
    multiple_log_writer & m_writer;
};
 
 
typedef basic_internal_thread_safe_log_sharethread< char> internal_thread_safe_log_sharethread;
typedef basic_internal_thread_safe_log_sharethread< wchar_t> winternal_thread_safe_log_sharethread;
 
 
// END OF internal_thread_safe_log for SharedThread
// (multiple logs share the same thread for writing to them)
////////////////////////////////////////////////////////////////////
 
 
 
 
////////////////////////////////////////////////////////////////////
// internal_thread_safe_log for OwnThread
// (each log has its own thread for writing messages to it)
 
 
 
 
// allows thread-safe writing
template<
        class char_type,
        class traits_type = std::char_traits< char_type>,
        class thread_manager = DEFAULT_THREAD_MANAGER >
    class thread_safe_log_writer_ownthread
{
    typedef thread_safe_log_writer_ownthread< char_type, traits_type> this_class;
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    typedef std::basic_string< char_type, traits_type> string_type;
 
 
    // forward declaration
    struct thread_info;
    friend struct thread_info;
 
 
    // thread-related definitions
    typedef typename thread_manager::thread_obj_base thread_obj_base;
    typedef typename thread_manager::critical_section critical_section;
   typedef typename thread_manager::auto_lock_unlock auto_lock_unlock;
    // so that from our thread we know the object we're manipulating
    struct thread_info : public thread_obj_base
    {
        thread_info()
            : m_bHasFinished( false),
             m_pThis( NULL)
        {}
 
 
        /* virtual */ void operator()()
        {
            while ( true)
            {
                std::string * pstr = NULL;
                bool bDoFlush = false;
                {
                    auto_lock_unlock locker( m_pThis->m_cs);
                    // get the string
                    if ( m_pThis->m_astrMessages.size() > 0)
                    {
                        pstr = m_pThis->m_astrMessages.front();
                        m_pThis->m_astrMessages.pop();
                        // we flush only when there are no more messages to write
                        // (flushing could be time-consuming)
                        bDoFlush = ( m_pThis->m_astrMessages.size() == 0);
                    }
                    // ... only when there are no more messages,
                    //    will we ask if we should be destructed
                    else if ( m_pThis->m_bShouldBeDestructed)
                    {
                        // signal to the other thread we've finished
                        m_bHasFinished = true;
                        return;
                    }
                }
                // write the string
                if ( pstr)
                {
                    m_pThis->m_underlyingLog << *pstr;
                    if ( bDoFlush)
                        m_pThis->m_underlyingLog.flush();
                    delete pstr;
                }
                else
                    // nothing to write - wait
                   thread_manager::sleep( 1);              
            }
        }
 
 
        this_class * m_pThis;
        volatile bool m_bHasFinished;
    };
 
 
public:
    void add_message( const string_type & str)
    {
        auto_lock_unlock locker( m_cs);
        m_astrMessages.push( new string_type( str));
    }
 
 
    thread_safe_log_writer_ownthread( ostream_type & underlyingLog)
        : m_underlyingLog( underlyingLog),
          m_bShouldBeDestructed( false)
    {
        m_info.m_pThis = this;
        thread_manager::create_thread( m_info);
    }
 
 
    ~thread_safe_log_writer_ownthread()
    {
        // signal to the other thread we're about to be
        // destructed
        {
            auto_lock_unlock locker( m_cs);
            m_bShouldBeDestructed = true;
        }
        // wait while the other thread writes all messages
        while ( true)
        {
            auto_lock_unlock locker( m_cs);
            if ( m_info.m_bHasFinished)
                // the other thread has finished
               break;
        }
    }
 
 
    critical_section & cs() const { return m_cs; }
private:
    // the critical section used for thread-safe locking
    mutable critical_section m_cs;
 
 
    // needed to create the other thread
    thread_info m_info;
    volatile bool m_bShouldBeDestructed;
 
 
    ostream_type & m_underlyingLog;
    std::queue< string_type*> m_astrMessages;
};
 
 
 
 
template<
        class char_type,
        class traits_type = std::char_traits< char_type>,
        class thread_manager = DEFAULT_THREAD_MANAGER >
    class basic_internal_thread_safe_log_ownthread
        : public basic_internal_thread_safe_log_base< char_type, traits_type>
{
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    friend class basic_thread_safe_log< char_type, traits_type>;
    typedef thread_safe_log_writer_ownthread< char_type, traits_type, thread_manager> log_writer_ownthread;
    // non-copyiable
    typedef basic_internal_thread_safe_log_ownthread< char_type, traits_type> this_class;
    basic_internal_thread_safe_log_ownthread( const this_class &);
    this_class & operator=( this_class &);
 
 
    // thread-related definitions
    typedef typename thread_manager::auto_lock_unlock auto_lock_unlock;
 
 
public:
    basic_internal_thread_safe_log_ownthread( ostream_type & underlyingLog)
        : m_underlyingLog( underlyingLog),
          m_writer( underlyingLog)
    {}
 
 
    ~basic_internal_thread_safe_log_ownthread()
    {}
 
 
    void write_message( const std::basic_string< char_type, traits_type> & str)
    { m_writer.add_message( str); }
 
 
    void copy_state_to( ostream_type & dest) const
    {
        auto_lock_unlock locker( m_writer.cs());
        dest.copyfmt( m_underlyingLog);
        dest.setstate( m_underlyingLog.rdstate());
    }
 
 
    void copy_state_from( const ostream_type & src)
    {
        auto_lock_unlock locker( m_writer.cs());
        m_underlyingLog.copyfmt( src);
        m_underlyingLog.setstate( m_underlyingLog.rdstate());
    }
 
 
private:
    ostream_type & m_underlyingLog;
   log_writer_ownthread m_writer;
};
 
 
typedef basic_internal_thread_safe_log_ownthread< char> internal_thread_safe_log_ownthread;
typedef basic_internal_thread_safe_log_ownthread< wchar_t> winternal_thread_safe_log_ownthread;
 
 
 
 
// END OF internal_thread_safe_log for OwnThread
// (each log has its own thread for writing messages to it)
////////////////////////////////////////////////////////////////////
 
 
 
 
////////////////////////////////////////////////////////////////////
// thread_safe_log class;
// the class we should return from our get_log() functions.
 
 
 
 
// helper used on debug mode - to allow catching mistakes:
// using temporaries after they've been destructed
template< class char_type, class traits_type>
class invalid_streambuf : public std::basic_streambuf< char_type, traits_type>
{
#ifndef __GNUC__
    typedef std::basic_streambuf< char_type, traits_type> base_class;
    using typename base_class::int_type;
#endif
protected:
    void bad_call()
    {
       /*
        Mistake: most likely, you've done something like:
 
 
        std::ostream & out = get_log().ts();
        // note: here, the temporary returned by get_log().ts() has
        //      been destructed !!!
        out << "bla bla" << std::endl;
 
 
 
 
        Use the following instead:
        thread_safe_log out = get_log();
        out.ts() << "bla bla" << std::endl;
        */
        assert( false);
    }
 
 
    // ... called on flush()
    virtual int sync()
    { bad_call(); return 0; }
 
 
    virtual int_type overflow(int_type nChar)
    { bad_call(); return 0; }
 
 
    virtual std::streamsize xsputn(const char_type *S, std::streamsize N)
    { bad_call(); return 0; }
 
 
public:
    static invalid_streambuf< char_type, traits_type> s_instance;
};
 
 
// initialize the one and only instance
template< class char_type, class traits_type>
invalid_streambuf< char_type, traits_type> invalid_streambuf< char_type, traits_type>::s_instance;
 
 
 
 
 
 
 
 
template< class char_type, class traits_type>
    class basic_thread_safe_log
    // *** protected, not public !!!
    : protected basic_message_handler_log< char_type, traits_type>
    
{
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    // hold reference to base basic_internal_thread_safe_log;
    // it could be basic_internal_thread_safe_log_sharethread,
    // basic_internal_thread_safe_log_ownthread, etc.
    typedef basic_internal_thread_safe_log_base< char_type, traits_type> internal_type;
 
 
#ifndef __GNUC__
    typedef basic_message_handler_log< char_type, traits_type> base_class;
    using typename base_class::string_type;
#endif
public:
    basic_thread_safe_log( internal_type & tsLog)
        : m_tsLog( tsLog)
    {
        // get underlying stream state
        tsLog.copy_state_to( ts() );
    }
  
    basic_thread_safe_log( const basic_thread_safe_log< char_type, traits_type> & from)
        : m_tsLog( from.m_tsLog),
          // ... on some platforms, a std::ostream base copy-constructor
          //    might be defined as private...
         basic_message_handler_log< char_type, traits_type>()
    {
        // get underlying stream state
        m_tsLog.copy_state_to( ts() );
    }
 
 
    ~basic_thread_safe_log()
    {
        // copy state to underlying stream
        m_tsLog.copy_state_from( ts() );
        this->write_last_message( *this);
 
 
#ifndef NDEBUG
        // debug-mode
        this->rdbuf( &(invalid_streambuf< char_type, traits_type>::s_instance));
#endif
    }
 
 
    // get base class - to which we can write
   std::basic_ostream< char_type, traits_type> & ts()
    { return *this; }
 
 
    void on_last_message( const string_type & str)
    {
        // don't forget to flush the stream before it's destructed!!!
        // (the easiest way is to append 'std::endl' to it
        assert ( str.empty());
    }
 
 
protected:
    virtual void on_new_message( const string_type & str)
    {
        if ( str.empty())
            return;
        m_tsLog.write_message( str);
    }
 
 
 
 
private:
    internal_type  & m_tsLog;
};
 
 
typedef basic_thread_safe_log< char> thread_safe_log;
typedef basic_thread_safe_log< wchar_t> wthread_safe_log;
 
 
// END OF thread_safe_log class;
// the class we should return from our get_log() functions.
////////////////////////////////////////////////////////////////////
 
 
 
 
//////////////////////////////////////////////////////////
// Test
 
 
#include <iostream>
#include <fstream>
#include <iomanip>
 
 
const int THREADS_COUNT = 200;
const int WRITES_PER_THREAD = 500;
 
 
// the writer
thread_safe_log_writer_sharethread & get_ts_writer()
{
    static thread_safe_log_writer_sharethread writer;
    return writer;
}
 
 
template< int i> struct int_to_type { int_to_type() {} };
 
 
// return out<idx>.txt
// (example: for 3, return 'out3.txt')
template< int idxLog> std::string get_out_name(
        bool bIsSharedLog,
        int_to_type< idxLog> * = NULL /* workaround for VC6 bug */)
{
    std::ostringstream out;
    out << "out" << (bIsSharedLog ? "sharedthread" : "ownthread")
        << idxLog << ".txt";
    return out.str();
}
 
 
// (Shared Thread) we have 10 logs
// log <idx> has priority (<idx>+1)^2 * 10
// (example: log 6 has priority 490)
template< int idxLog>
thread_safe_log templ_get_log_sharedthread(        int_to_type< idxLog> * = NULL /* workaround for VC6 bug */){    static std::ofstream out( get_out_name< idxLog>( true).c_str() );    static internal_thread_safe_log_sharethread log( out, get_ts_writer(), 10 * ( idxLog + 1) * ( idxLog + 1));    return thread_safe_log( log);}  // (Own Thread) we have 10 logs// each log has its own thread for writing to ittemplate< int idxLog>thread_safe_log templ_get_log_ownthread(        int_to_type< idxLog> * = NULL /* workaround for VC6 bug */){    static std::ofstream out( get_out_name< idxLog>( false).c_str() );    static internal_thread_safe_log_ownthread log( out);    return thread_safe_log( log);}      // based on the index, return a different log//// first 10 logs - logs sharing the same thread// last 10 logs  - each log with its own threadthread_safe_log get_log( int idxLog){    switch( idxLog)    {    case 0: return templ_get_log_sharedthread< 0>();    case 1: return templ_get_log_sharedthread< 1>();    case 2: return templ_get_log_sharedthread< 2>();    case 3: return templ_get_log_sharedthread< 3>();    case 4: return templ_get_log_sharedthread< 4>();    case 5: return templ_get_log_sharedthread< 5>();    case 6: return templ_get_log_sharedthread< 6>();    case 7: return templ_get_log_sharedthread< 7>();    case 8: return templ_get_log_sharedthread< 8>();    case 9: return templ_get_log_sharedthread< 9>();      case 10: return templ_get_log_ownthread< 0>();    case 11: return templ_get_log_ownthread< 1>();    case 12: return templ_get_log_ownthread< 2>();    case 13: return templ_get_log_ownthread< 3>();    case 14: return templ_get_log_ownthread< 4>();    case 15: return templ_get_log_ownthread< 5>();    case 16: return templ_get_log_ownthread< 6>();    case 17: return templ_get_log_ownthread< 7>();    case 18: return templ_get_log_ownthread< 8>();    case 19: return templ_get_log_ownthread< 9>();      default: assert( false); return templ_get_log_sharedthread< 0>();    }}  #include <windows.h>LONG nRemainingThreads = THREADS_COUNT;DWORD WINAPI WriteToLog( LPVOID lpData){    int *pnThreadID = ( int *)lpData;    int idxLog = *pnThreadID % 20;    // wait for all threads to be created, so that    // we write at about the same time (stress it ;-))    Sleep( 500);      for ( int idx = 0; idx < WRITES_PER_THREAD; idx++)    {        get_log( idxLog).ts() << "writing double: " << 5.23 << std::endl;        get_log( idxLog).ts() << "message " << idx << " from thread " << *pnThreadID << std::endl;        // ... get other threads a chance to write        Sleep( 1);          if ( ( idx == 10) && ( *pnThreadID == 10))        {            // from now on, '5.23' will be written as '5,23'            // (german locale)            std::locale loc = std::locale( "german");            get_log( idxLog).ts().imbue( loc);        }      }      InterlockedDecrement( &nRemainingThreads);    delete pnThreadID;    return 0;}  int main(int argc, char* argv[]){    // make sure the statics are initialized    get_ts_writer();    get_log( 0);    get_log( 1); get_log( 2); get_log( 3); get_log( 4);    get_log( 5); get_log( 6); get_log( 7); get_log( 8); get_log( 9);    get_log( 10); get_log( 11); get_log( 12); get_log( 13); get_log( 14);    get_log( 15); get_log( 16); get_log( 17); get_log( 18); get_log( 19);      // would cause an assertion - forgot to write std::endl to the stream!    // get_log( 0).ts() << "test";      // the following should generate an assertion:    // writing to temporary after it's been destructed    //    // std::ostream & out1 = get_log( 0).ts();    // out1 << "blabla" << std::endl;    // std::ostream & out2 = get_log( 0).ts();    // out2 << std::endl;        for ( int idx = 0; idx < THREADS_COUNT; ++idx)    {        DWORD dwThreadID;        CreateThread( 0, 0, WriteToLog, new int( idx), 0, &dwThreadID);    }      // wait for all threads to end    while ( true)    {        InterlockedIncrement( &nRemainingThreads);        if ( InterlockedDecrement( &nRemainingThreads) == 0)            break;        Sleep( 100);    }    return 0;}