and why can't listings be included in "Printer Friendly" output?
--Jim
Discussion on:
View:
Show:
Thanks for pointing it out, I'll talk to my editor.
Also, for the source listings - they are pretty big, so including them in the listing is not an option.
Try www.torjo.com/code/ts-logging.zip
Best,
John
Also, for the source listings - they are pretty big, so including them in the listing is not an option.
Try www.torjo.com/code/ts-logging.zip
Best,
John
A very nice article.
Could you also provide the source code. Zip file availble at www.torjo.com/code is corrupted and doesn't open.
Could you also provide the source code. Zip file availble at www.torjo.com/code is corrupted and doesn't open.
There's a bug in MS VS2010
http://support.microsoft.com/kb/240862
that gives the following error when I try to compile:
c:\c++\logger\logger\message_handler_log.h(184): error C2555: 'basic_message_handler_log_streambuf ::showmanyc': overriding virtual function return type differs and is not covariant from 'std::basic_streambuf traits_type=std::char_traits _Traits=std::char_traits c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(346) : see declaration of 'std::basic_streambuf _Traits=std::char_traits c:\c++\logger\logger\message_handler_log.h(249) : see reference to class template instantiation 'basic_message_handler_log_streambuf traits_type=std::char_traits c:\c++\logger\logger\logger.cpp(872) : see reference to class template instantiation 'basic_message_handler_log c:\c++\logger\logger\logger.cpp(1018) : see reference to class template instantiation 'basic_thread_safe_log ]
I'm working on a fix for it and will post if I find it.
----------------------
HERE'S THE SOURCE I'M USING:
----------------
//Listing K
// message_handler_log.h
#ifndef MESSAGE_HANDLER_LOG_H
#define MESSAGE_HANDLER_LOG_H
#include
#include
#include
#include
// forward declaration(s)
template class char_type, class traits_type = std::char_traits
class basic_message_handler_log;
// represents the stream buffer for a message_handler_log (see below)
// Note: NOT thread-safe
template class char_type , class traits_type = std::char_traits
class basic_message_handler_log_streambuf
: public std::basic_streambuf
{
private:
typedef basic_message_handler_log_streambuf this_class;
friend class basic_message_handler_log ;
typedef std::basic_streambuf streambuf_type;
typedef basic_message_handler_log ostream_type;
// base class
typedef std::basic_streambuf on_new_message( get_stream_buffer().str() );
m_pStreamBuffer = std::auto_ptr setp( NULL, NULL);
return this;
}
// write the characters from the buffer
// to their real destination
virtual int_type overflow(int_type nChar = traits_type::eof())
{
if ( traits_type::not_eof( nChar))
get_stream_buffer() ( char_type)nChar;
return traits_type::not_eof( nChar);
}
virtual std::streamsize xsputn(const char_type *S, std::streamsize N)
{
get_stream_buffer().write( S, N);
return N;
}
public:
basic_message_handler_log_streambuf()
: m_pStreamBuffer( new string_stream_type),
m_bLastMessageWritten( false)
{}
basic_message_handler_log_streambuf( const this_class & )
: m_pStreamBuffer( new string_stream_type),
m_bLastMessageWritten( false)
{}
~basic_message_handler_log_streambuf()
{
// from your basic_message_handler_log'
// derived class' destructor, make sure you call
// 'write_last_message( *this);' !!!
assert( m_bLastMessageWritten);
}
private:
typedef std::basic_ostringstream string_stream_type;
string_stream_type & get_stream_buffer() { return *m_pStreamBuffer; }
template
void process_last_message( type & val)
{
if ( m_bLastMessageWritten)
return;
m_bLastMessageWritten = true;
val.on_last_message( get_stream_buffer().str());
}
private:
// holds the Message, until it's flushed
std::auto_ptr m_pStreamBuffer;
// the Message Handler Log - where we write into
ostream_type * m_pOwnerStream;
// has the last message been written?
// if this is false, the last sync() has not been done.
bool m_bLastMessageWritten;
};
// derive your class from this, and implement the PROTECTED on_new_message function
template
class basic_message_handler_log
: public std::basic_ostream
{
typedef basic_message_handler_log_streambuf handler_streambuf_type;
friend class basic_message_handler_log_streambuf ;
typedef std::basic_ostream base_class;
typedef std::basic_ostringstream string_stream_type;
protected:
typedef std::basic_string init( &m_StreamBuf);
m_StreamBuf.pubsetbuf( NULL, 0);
}
basic_message_handler_log( const basic_message_handler_log init( &m_StreamBuf);
m_StreamBuf.pubsetbuf( NULL, 0);
}
// from your derived class' destructor, make sure you call
// 'write_last_message( *this);' !!!
virtual ~basic_message_handler_log() {}
protected:
virtual void on_new_message( const string_type & str) = 0;
void on_last_message( const string_type & str)
{
// default behavior
on_new_message( str);
}
template
void write_last_message( type & val)
{
m_StreamBuf.process_last_message( val);
}
public:
// our stream buffer
handler_streambuf_type m_StreamBuf;
};
typedef basic_message_handler_log message_handler_log;
typedef basic_message_handler_log wmessage_handler_log;
#endif
----------------------------------
----------------------
// Logger.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
//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
#include
#include
#include
#include
#include
// 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
// 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 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
//
#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
class basic_thread_safe_log;
// base class for our internal thread_safe_log object
template
class basic_internal_thread_safe_log_base
{
typedef basic_internal_thread_safe_log_base this_class;
typedef typename std::basic_ostream ostream_type;
friend class basic_thread_safe_log ;
// 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 & 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
class basic_internal_thread_safe_log_sharethread;
// allows thread-safe writing for multiple logs
template
class char_type,
class traits_type = std::char_traits
class basic_thread_safe_log_writer_sharethread
{
typedef basic_thread_safe_log_writer_sharethread this_class;
typedef std::basic_ostream ostream_type;
typedef std::basic_string string_type;
friend class basic_internal_thread_safe_log_sharethread ;
// 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 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 0)
{
std::vector ::iterator
first = astrMsgs.begin(), last = astrMsgs.end();
while ( first != last)
{
std::string *pstr = *first;
*pLog 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 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 m_aWritesTo;
// for each log, what should we write to it?
typedef std::map 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 thread_safe_log_writer_sharethread;
typedef basic_thread_safe_log_writer_sharethread wthread_safe_log_writer_sharethread;
// multiple basic_internal_thread_safe logs share the same thread,
// which writes to them
template
class basic_internal_thread_safe_log_sharethread
: public basic_internal_thread_safe_log_base
{
typedef basic_internal_thread_safe_log_sharethread this_class;
typedef typename std::basic_ostream ostream_type;
typedef class basic_thread_safe_log_writer_sharethread 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 & 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 internal_thread_safe_log_sharethread;
typedef basic_internal_thread_safe_log_sharethread 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
class thread_safe_log_writer_ownthread
{
typedef thread_safe_log_writer_ownthread this_class;
typedef std::basic_ostream ostream_type;
typedef std::basic_string m_underlyingLog 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 m_astrMessages;
};
template
class char_type,
class traits_type = std::char_traits
class basic_internal_thread_safe_log_ownthread
: public basic_internal_thread_safe_log_base
{
typedef std::basic_ostream ostream_type;
friend class basic_thread_safe_log ;
typedef thread_safe_log_writer_ownthread log_writer_ownthread;
// non-copyiable
typedef basic_internal_thread_safe_log_ownthread 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 & 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 internal_thread_safe_log_ownthread;
typedef basic_internal_thread_safe_log_ownthread 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 invalid_streambuf : public std::basic_streambuf
{
#ifndef __GNUC__
typedef std::basic_streambuf 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 s_instance;
};
// initialize the one and only instance
template
invalid_streambuf invalid_streambuf ::s_instance;
template
class basic_thread_safe_log
// *** protected, not public !!!
: protected basic_message_handler_log
{
typedef std::basic_ostream 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 internal_type;
#ifndef __GNUC__
typedef basic_message_handler_log 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 & from)
: m_tsLog( from.m_tsLog),
// ... on some platforms, a std::ostream base copy-constructor
// might be defined as private...
basic_message_handler_log rdbuf( &(invalid_streambuf ::s_instance));
#endif
}
// get base class - to which we can write
std::basic_ostream & 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 thread_safe_log;
typedef basic_thread_safe_log wthread_safe_log;
// END OF thread_safe_log class;
// the class we should return from our get_log() functions.
////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// Test
#include
#include
#include
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 struct int_to_type { int_to_type() {} };
// return out .txt
// (example: for 3, return 'out3.txt')
template std::string get_out_name(
bool bIsSharedLog,
int_to_type * = 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 has priority ( +1)^2 * 10
// (example: log 6 has priority 490)
template
thread_safe_log templ_get_log_sharedthread( int_to_type * = NULL /* workaround for VC6 bug */)
{
static std::ofstream out( get_out_name ( 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 it
template thread_safe_log templ_get_log_ownthread(int_to_type * = NULL /* workaround for VC6 bug */)
{
static std::ofstream out( get_out_name ( 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 thread
thread_safe_log get_log( int idxLog)
{
switch( idxLog)
{
case 0: return templ_get_log_sharedthread ();
case 1: return templ_get_log_sharedthread ();
case 2: return templ_get_log_sharedthread ();
case 3: return templ_get_log_sharedthread ();
case 4: return templ_get_log_sharedthread ();
case 5: return templ_get_log_sharedthread ();
case 6: return templ_get_log_sharedthread ();
case 7: return templ_get_log_sharedthread ();
case 8: return templ_get_log_sharedthread ();
case 9: return templ_get_log_sharedthread ();
case 10: return templ_get_log_ownthread ();
case 11: return templ_get_log_ownthread ();
case 12: return templ_get_log_ownthread ();
case 13: return templ_get_log_ownthread ();
case 14: return templ_get_log_ownthread ();
case 15: return templ_get_log_ownthread ();
case 16: return templ_get_log_ownthread ();
case 17: return templ_get_log_ownthread ();
case 18: return templ_get_log_ownthread ();
case 19: return templ_get_log_ownthread ();
default: assert( false);
return templ_get_log_sharedthread ();
}}
#include
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;}
//Entry Point
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;}
http://support.microsoft.com/kb/240862
that gives the following error when I try to compile:
c:\c++\logger\logger\message_handler_log.h(184): error C2555: 'basic_message_handler_log_streambuf ::showmanyc': overriding virtual function return type differs and is not covariant from 'std::basic_streambuf traits_type=std::char_traits _Traits=std::char_traits c:\program files (x86)\microsoft visual studio 10.0\vc\include\streambuf(346) : see declaration of 'std::basic_streambuf _Traits=std::char_traits c:\c++\logger\logger\message_handler_log.h(249) : see reference to class template instantiation 'basic_message_handler_log_streambuf traits_type=std::char_traits c:\c++\logger\logger\logger.cpp(872) : see reference to class template instantiation 'basic_message_handler_log c:\c++\logger\logger\logger.cpp(1018) : see reference to class template instantiation 'basic_thread_safe_log ]
I'm working on a fix for it and will post if I find it.
----------------------
HERE'S THE SOURCE I'M USING:
----------------
//Listing K
// message_handler_log.h
#ifndef MESSAGE_HANDLER_LOG_H
#define MESSAGE_HANDLER_LOG_H
#include
#include
#include
#include
// forward declaration(s)
template class char_type, class traits_type = std::char_traits
class basic_message_handler_log;
// represents the stream buffer for a message_handler_log (see below)
// Note: NOT thread-safe
template class char_type , class traits_type = std::char_traits
class basic_message_handler_log_streambuf
: public std::basic_streambuf
{
private:
typedef basic_message_handler_log_streambuf this_class;
friend class basic_message_handler_log ;
typedef std::basic_streambuf streambuf_type;
typedef basic_message_handler_log ostream_type;
// base class
typedef std::basic_streambuf on_new_message( get_stream_buffer().str() );
m_pStreamBuffer = std::auto_ptr setp( NULL, NULL);
return this;
}
// write the characters from the buffer
// to their real destination
virtual int_type overflow(int_type nChar = traits_type::eof())
{
if ( traits_type::not_eof( nChar))
get_stream_buffer() ( char_type)nChar;
return traits_type::not_eof( nChar);
}
virtual std::streamsize xsputn(const char_type *S, std::streamsize N)
{
get_stream_buffer().write( S, N);
return N;
}
public:
basic_message_handler_log_streambuf()
: m_pStreamBuffer( new string_stream_type),
m_bLastMessageWritten( false)
{}
basic_message_handler_log_streambuf( const this_class & )
: m_pStreamBuffer( new string_stream_type),
m_bLastMessageWritten( false)
{}
~basic_message_handler_log_streambuf()
{
// from your basic_message_handler_log'
// derived class' destructor, make sure you call
// 'write_last_message( *this);' !!!
assert( m_bLastMessageWritten);
}
private:
typedef std::basic_ostringstream string_stream_type;
string_stream_type & get_stream_buffer() { return *m_pStreamBuffer; }
template
void process_last_message( type & val)
{
if ( m_bLastMessageWritten)
return;
m_bLastMessageWritten = true;
val.on_last_message( get_stream_buffer().str());
}
private:
// holds the Message, until it's flushed
std::auto_ptr m_pStreamBuffer;
// the Message Handler Log - where we write into
ostream_type * m_pOwnerStream;
// has the last message been written?
// if this is false, the last sync() has not been done.
bool m_bLastMessageWritten;
};
// derive your class from this, and implement the PROTECTED on_new_message function
template
class basic_message_handler_log
: public std::basic_ostream
{
typedef basic_message_handler_log_streambuf handler_streambuf_type;
friend class basic_message_handler_log_streambuf ;
typedef std::basic_ostream base_class;
typedef std::basic_ostringstream string_stream_type;
protected:
typedef std::basic_string init( &m_StreamBuf);
m_StreamBuf.pubsetbuf( NULL, 0);
}
basic_message_handler_log( const basic_message_handler_log init( &m_StreamBuf);
m_StreamBuf.pubsetbuf( NULL, 0);
}
// from your derived class' destructor, make sure you call
// 'write_last_message( *this);' !!!
virtual ~basic_message_handler_log() {}
protected:
virtual void on_new_message( const string_type & str) = 0;
void on_last_message( const string_type & str)
{
// default behavior
on_new_message( str);
}
template
void write_last_message( type & val)
{
m_StreamBuf.process_last_message( val);
}
public:
// our stream buffer
handler_streambuf_type m_StreamBuf;
};
typedef basic_message_handler_log message_handler_log;
typedef basic_message_handler_log wmessage_handler_log;
#endif
----------------------------------
----------------------
// Logger.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
//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
#include
#include
#include
#include
#include
// 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
// 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 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
//
#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
class basic_thread_safe_log;
// base class for our internal thread_safe_log object
template
class basic_internal_thread_safe_log_base
{
typedef basic_internal_thread_safe_log_base this_class;
typedef typename std::basic_ostream ostream_type;
friend class basic_thread_safe_log ;
// 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 & 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
class basic_internal_thread_safe_log_sharethread;
// allows thread-safe writing for multiple logs
template
class char_type,
class traits_type = std::char_traits
class basic_thread_safe_log_writer_sharethread
{
typedef basic_thread_safe_log_writer_sharethread this_class;
typedef std::basic_ostream ostream_type;
typedef std::basic_string string_type;
friend class basic_internal_thread_safe_log_sharethread ;
// 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 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 0)
{
std::vector ::iterator
first = astrMsgs.begin(), last = astrMsgs.end();
while ( first != last)
{
std::string *pstr = *first;
*pLog 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 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 m_aWritesTo;
// for each log, what should we write to it?
typedef std::map 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 thread_safe_log_writer_sharethread;
typedef basic_thread_safe_log_writer_sharethread wthread_safe_log_writer_sharethread;
// multiple basic_internal_thread_safe logs share the same thread,
// which writes to them
template
class basic_internal_thread_safe_log_sharethread
: public basic_internal_thread_safe_log_base
{
typedef basic_internal_thread_safe_log_sharethread this_class;
typedef typename std::basic_ostream ostream_type;
typedef class basic_thread_safe_log_writer_sharethread 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 & 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 internal_thread_safe_log_sharethread;
typedef basic_internal_thread_safe_log_sharethread 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
class thread_safe_log_writer_ownthread
{
typedef thread_safe_log_writer_ownthread this_class;
typedef std::basic_ostream ostream_type;
typedef std::basic_string m_underlyingLog 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 m_astrMessages;
};
template
class char_type,
class traits_type = std::char_traits
class basic_internal_thread_safe_log_ownthread
: public basic_internal_thread_safe_log_base
{
typedef std::basic_ostream ostream_type;
friend class basic_thread_safe_log ;
typedef thread_safe_log_writer_ownthread log_writer_ownthread;
// non-copyiable
typedef basic_internal_thread_safe_log_ownthread 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 & 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 internal_thread_safe_log_ownthread;
typedef basic_internal_thread_safe_log_ownthread 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 invalid_streambuf : public std::basic_streambuf
{
#ifndef __GNUC__
typedef std::basic_streambuf 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 s_instance;
};
// initialize the one and only instance
template
invalid_streambuf invalid_streambuf ::s_instance;
template
class basic_thread_safe_log
// *** protected, not public !!!
: protected basic_message_handler_log
{
typedef std::basic_ostream 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 internal_type;
#ifndef __GNUC__
typedef basic_message_handler_log 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 & from)
: m_tsLog( from.m_tsLog),
// ... on some platforms, a std::ostream base copy-constructor
// might be defined as private...
basic_message_handler_log rdbuf( &(invalid_streambuf ::s_instance));
#endif
}
// get base class - to which we can write
std::basic_ostream & 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 thread_safe_log;
typedef basic_thread_safe_log wthread_safe_log;
// END OF thread_safe_log class;
// the class we should return from our get_log() functions.
////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// Test
#include
#include
#include
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 struct int_to_type { int_to_type() {} };
// return out .txt
// (example: for 3, return 'out3.txt')
template std::string get_out_name(
bool bIsSharedLog,
int_to_type * = 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 has priority ( +1)^2 * 10
// (example: log 6 has priority 490)
template
thread_safe_log templ_get_log_sharedthread( int_to_type * = NULL /* workaround for VC6 bug */)
{
static std::ofstream out( get_out_name ( 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 it
template thread_safe_log templ_get_log_ownthread(int_to_type * = NULL /* workaround for VC6 bug */)
{
static std::ofstream out( get_out_name ( 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 thread
thread_safe_log get_log( int idxLog)
{
switch( idxLog)
{
case 0: return templ_get_log_sharedthread ();
case 1: return templ_get_log_sharedthread ();
case 2: return templ_get_log_sharedthread ();
case 3: return templ_get_log_sharedthread ();
case 4: return templ_get_log_sharedthread ();
case 5: return templ_get_log_sharedthread ();
case 6: return templ_get_log_sharedthread ();
case 7: return templ_get_log_sharedthread ();
case 8: return templ_get_log_sharedthread ();
case 9: return templ_get_log_sharedthread ();
case 10: return templ_get_log_ownthread ();
case 11: return templ_get_log_ownthread ();
case 12: return templ_get_log_ownthread ();
case 13: return templ_get_log_ownthread ();
case 14: return templ_get_log_ownthread ();
case 15: return templ_get_log_ownthread ();
case 16: return templ_get_log_ownthread ();
case 17: return templ_get_log_ownthread ();
case 18: return templ_get_log_ownthread ();
case 19: return templ_get_log_ownthread ();
default: assert( false);
return templ_get_log_sharedthread ();
}}
#include
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;}
//Entry Point
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;}
This code is on Builder.com and the one place that it doesn't compile is.....Borland Builder.
Thanks anyway; will write thread-safe logger that compiles...
Thanks anyway; will write thread-safe logger that compiles...
- Keyboard Shortcuts:
- Prev
- Next
- Toggle









































