I have become quite a fan of defining inserter (operator<<) methods for my C++ classes. Output becomes so simple just: os << instance_of_my_class. I rewrote my program logging to use an ogzstream class and custom inserters for "almost everything". When I run into problems I just have to add a short log statement and see the results. I would always run in to problems using the Visual C++ TRACE macros which use a printf type of format string. There's a (quite small) limit on the string buffer which causes an ASSERT if it overflows. And it isn't typesafe whereas with operator<< you can output almost anything and if the compiler can't find a << operator for a class it will complain. TRACE will merrily compile anything and then ASSERT or crash if the item isn't a CString or a simple type like int or double. The debug and logging macros I use are shown below. The CError::stream() and CError::gzlog() are static methods which define static variables internally so that I can do logging and stream formatting at any time during my program — even during static class variable initialization. In effect I'm using singletons.
// Usage: GLOBAL_MSG(FILE_LINE, fmt, arguments...) ;
#define FILE_LINE THIS_FILE, __LINE__
#define GLOBAL_MSG CError::global_msg
// Pre-format an inserter expression msg with a date-time stamp, a keyword,
// the source code file and the line number.
#define INSERTMSG(keyword,msg) \
CError::now() \
<< " " << keyword << " " \
<< CSplitPath::get_filename(THIS_FILE) \
<< ":" << __LINE__ \
<< " " << msg
// Usage: LOGMSG("result", "The result is " << result) ;
#define LOGMSG(keyword,msg) CError::gzlog() << INSERTMSG(keyword,msg) \
<< std::endl
//
#define POPUPMSG(x) CError::stream() << x ; \
CError::popup_msg(CError::stream().str().c_str(), 0, \
CSplitPath::get_filename(THIS_FILE).c_str(), __LINE__) ; \
CError::stream().str("")
#ifdef _DEBUG
// Fancy macros to generate debug or trace listing in the log file as well
// as a trace message. It is wrapped in a CCriticalSection because TRACEZ
// messages may come from different execution threads and the CError::stream()
// is shared among them. There are two std::endl, one to flush and send the
// line to the log file and the other to new line terminate the string.
//
// The if ... TRACEBUFSIZE works around a TRACE0 bug as TRACE0(string)
// resolves to AfxTrace("%s", string) and there's a 4096 character buffer
// limit which causes an assert if string is more than this number of
// characters.
//
// Arguments: (keyword,expression,inserter)
// keyword - a string e.g. "trace"
// expression - if (expression) is true send inserter to file and debug
// output otherwise just use TRACE0
// inserter - C++ << expression
//
// Usage: TRACEMSG("trace",1,"The value is " << value) ;
//
// Reasons:
// 1) You can't imbed inserters in function calls though I have tried. :-)
// 2) Uses TRACE0 so that no formatting is done. In some cases formatting
// may overflow whatever buffer visual C++ provides causing an assert
// which isn't related to the program.
// 3) Completely disappears when _DEBUG not defined.
# define TRACEBUFSIZE 230
# define TRACEMSG(keyword,logfile,x) do \
{ \
CriticalSectionLock cs_lock ; \
CError::stream() << INSERTMSG(keyword,x) ; \
if (logfile) \
{ \
CError::gzlog() << CError::stream().str() << std::endl ; \
} \
if (CError::stream().str().size() > TRACEBUFSIZE) \
{ \
std::string str(CError::stream().str(), 0, TRACEBUFSIZE) ; \
str += "...\n" ; \
TRACE0(str.c_str()) ; \
} \
else \
{ \
CError::stream() << std::endl ; \
TRACE0(CError::stream().str().c_str()) ; \
} \
CError::stream().str("") ; \
} while(0)
# define TRACEZ(x) TRACEMSG("trace",1,x)
// Trace with no file logging.
# define TRACENZ(x) TRACEMSG("trace",0,x)
# define TRACEIF(condition,x) if (condition) \
{ \
TRACEMSG("trace",1,x) ; \
}
# define TRACEIFELSE(condition,x,y) if (condition) \
{ \
TRACEMSG("trace",1,x) ; \
} else { \
TRACEMSG("trace",1,y) ; \
}
// Log info messages to trace output as well as log file.
# define INFOMSG(x) TRACEMSG("info",1,x)
# define WARN(x) TRACEMSG("WARN",1,x)
# if defined(ENABLE_VERBOSE) && ENABLE_VERBOSE
# define VERBOZE(x) TRACEMSG("verbose",1,x)
# else // ENABLE_VERBOSE
# define VERBOZE(x)
# endif // ENABLE_VERBOSE
#else // _DEBUG
// Some macros now cause their arguments to disappear and insert nothing.
# define TRACEZ(x)
# define TRACENZ(x)
# define INFOMSG(x) LOGMSG("info",x)
# define WARN(x) LOGMSG("WARN",x)
# define TRACEIF(condition,x)
# define TRACEIFELSE(condition,x,y)
# define TRACEMSG(keyword,x)
# define VERBOZE(x)
#endif // _DEBUG
#define GZLOG(x) INFOMSG(x)
Copyright © 2002-2006 James (Jim) R. R. Service (@gmail.com - jservice)