Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Error logging mechansim? - aka using stream vs sprintf

1 view
Skip to first unread message

Dan Oelke

unread,
Jan 8, 1999, 3:00:00 AM1/8/99
to

In the past I have created error logging mechanisms that
looked something like this:

void SWFault(char * file,int line,int severity,char * fmt, ...);
(plus a little macro magic to hide __FILE__ and __LINE__
from the user of this function)

The SWFault function would look at the severity and if it was
met some filter criterian (usually a variable that could be changed at
run time to allow debugging) then it would call vsprintf() with the fmt
and
the variable arg list. The resulting string could be spit out a serial
port of an embedded system, or logged to disk or RAM, etc.
I would like to do the same basic concept of being able to log
various formatted error messages, but was thinking of how I
could use strstream to make stuff more typesafe, etc.

I could just make something like
SWFault(char * file, int line, int severity, char * data);
which would be invoked as
ostrstream t;
t << "Something died - " << errno << ends;
SWFault(__FILE__,__LINE__,FATAL, t.str() );

But, there are a couple of things I don't like.
1) Most importantly I do not want to format up the data
if I am not going to be storing it anyways.
2) I'ld like to keep everything in a single line/statement.
3) there is probably a better way to pass the string into
the SWFault() function - I don't know enough about C++ strings and
definetly not enough about streams to know how.

So, any ideas?

Thanks,
Dan


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Vaclav Barta

unread,
Jan 9, 1999, 3:00:00 AM1/9/99
to
Dan Oelke wrote:
>
> In the past I have created error logging mechanisms that
> looked something like this:
>
> void SWFault(char * file,int line,int severity,char * fmt, ...);
> (plus a little macro magic to hide __FILE__ and __LINE__
> from the user of this function)
[snip]

> I could just make something like
> SWFault(char * file, int line, int severity, char * data);
> which would be invoked as
> ostrstream t;
> t << "Something died - " << errno << ends;
> SWFault(__FILE__,__LINE__,FATAL, t.str() );
>
> But, there are a couple of things I don't like.
> 1) Most importantly I do not want to format up the data
> if I am not going to be storing it anyways.
> 2) I'ld like to keep everything in a single line/statement.
> 3) there is probably a better way to pass the string into
> the SWFault() function - I don't know enough about C++ strings and
> definetly not enough about streams to know how.

Keep your macros:
#define SWFAULT(swfault_local_param) \
do \
{ if (swfault_global_report_faults) \
{ stringstream t; \
t << swfault_local_param; \
ReportSWFault(__FILE__, __LINE__, t.str()); \
} \
} while (0)

And call them as:
SWFAULT("Something died - " << errno);

The macro is ugly, unmaintainable and unsafe (I've been
using a shorter name for swfault_local_param and has been
quite surprised when it clashed with a local variable name),
but it's defined just once, and the use is more convenient
and safer than vsprintf.

Hope this helps, and I'd be interested in suggestions for
improvement.

Bye
Vasek
--
I have a search engine, too!
http://www.locus.cz/locus/

Dan Oelke

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to

Vaclav Barta wrote:

> Keep your macros:
> #define SWFAULT(swfault_local_param) \
> do \
> { if (swfault_global_report_faults) \
> { stringstream t; \
> t << swfault_local_param; \
> ReportSWFault(__FILE__, __LINE__, t.str()); \
> } \
> } while (0)
>
> And call them as:
> SWFAULT("Something died - " << errno);
>
> The macro is ugly, unmaintainable and unsafe (I've been
> using a shorter name for swfault_local_param and has been
> quite surprised when it clashed with a local variable name),
> but it's defined just once, and the use is more convenient
> and safer than vsprintf.

Thanks - that is exactly the kind of thing I was looking for.

One further question - why the do { ... }while(0) loop around this macro?

Dan

Jason Smith

unread,
Jan 12, 1999, 3:00:00 AM1/12/99
to

Dan Oelke wrote in message <369A3A2B...@opticalsolutions.com>...
=> #define SWFAULT(swfault_local_param) \
=> do \
=> { if (swfault_global_report_faults) \
[...]
=> } while (0)
=>
=
=One further question - why the do { ... }while(0) loop around this macro?
=

So it can be invoked using a trailing semi-colon (which makes it look more
like standard C/C++ code).

Jason.


--
Hi, I'm Bob. I'm a tomato. I'm here to help you.
- Bob

Ian McCulloch

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

Dan Oelke wrote in message <3694F691...@opticalsolutions.com>...

>
>In the past I have created error logging mechanisms that
>looked something like this:
>
> void SWFault(char * file,int line,int severity,char * fmt, ...);
> (plus a little macro magic to hide __FILE__ and __LINE__
> from the user of this function)
>
>The SWFault function would look at the severity and if it was
>met some filter criterian (usually a variable that could be changed at
>run time to allow debugging) then it would call vsprintf() with the fmt
>and
>the variable arg list. The resulting string could be spit out a serial
>port of an embedded system, or logged to disk or RAM, etc.
>I would like to do the same basic concept of being able to log
>various formatted error messages, but was thinking of how I
>could use strstream to make stuff more typesafe, etc.
>
>I could just make something like
> SWFault(char * file, int line, int severity, char * data);
>which would be invoked as
> ostrstream t;
> t << "Something died - " << errno << ends;
> SWFault(__FILE__,__LINE__,FATAL, t.str() );
>
>But, there are a couple of things I don't like.
>1) Most importantly I do not want to format up the data
> if I am not going to be storing it anyways.
>2) I'ld like to keep everything in a single line/statement.
>3) there is probably a better way to pass the string into
> the SWFault() function - I don't know enough about C++ strings and
> definetly not enough about streams to know how.
>
>So, any ideas?


Hello.

Updating my diagnostic classes is getting fairly high on my "TO DO" list, so
recently I've been thinking a lot about how to do it. This is a
compile-time solution. In many ways this is a lot more useful, because
(with any luck!) the messages that do not meet the severity threshold will
be optimized out of the code.

I had originally thought that member function templates with explicit
parameters were required, but while I was actually writing this post, I
thought of an alternative! (My compiler currently does not support explicit
function template parameters). This code is not tested, and almost
certainly contains errors. I apologise in advance for the screw-ups. I
will try to implement this code in the next few days, and I will then report
back on whether it actually works! But basically the idea is

\begin{code}

//
// ErrorLogger
// Decides at compile time whether to output a diagnostic to a stream, or
ignore the line.
// Ignored diagnostics will hopefully then be optimized out of the code.
// ThresholdSeverity indicates the severity of messages that you want to
log.
// Messages with Severity <= ThresholdSeverity will be written to
*LogStream,
// Messages with Severity > ThresholdSeverity will be ignored.

template <int ThresholdSeverity, ostream* LogStream> class ErrorLogger
{
public:

template <int Severity>
class Log
{
public:
Log() {};

template <class T>
Log<Severity>& operator<<(const T& Object);
};

};

//
// This is the unspecialized version of Log<Severity>::operator<<. All it
does is delegate to
// Log<Severity-1>::operator<<. It is absolutely essential that this is
inlined, and optimized out
// by the compiler. This depends on using the member function version of
operator<<,
// so that we can call it on the tempoary object. (we couldnt bind the
tempoary to the non-const
// reference in a global operator<<.)
//

inline
template <int ThresholdSeverity, ostream* LogStream>
template <int Severity>
template <class T>
ErrorLogger<ThresholdSeverity, LogStream>::Log<Severity>&
ErrorLogger<ThresholdSeverity, LogStream>::Log<Severity>::operator<<
(
ErrorLogger<ThresholdSeverity, LogStream>::Log<Severity>& Stream,
const T& Object
)
{
ErrorLogger<ThresholdSeverity,
LogStream>::Log<Severity-1>().operator<<(Object);
return *this;
};

//
// If we hit Severity == 0, then we write the message to *LogStream

inline
template <int ThresholdSeverity, ostream* LogStream>
template<>
template <class T>
ErrorLogger<ThresholdSeverity, LogStream>::Log<0>&
ErrorLogger<ThresholdSeverity, LogStream>::Log<0>::operator<<
(
ErrorLogger<ThresholdSeverity, LogStream>::Log<Severity>& Stream,
const T& Object
)
{
*LogStream << Object;
return *this;
};

//
// If Severity > ThresholdSeverity, then we want to ignore the message

inline
template <int ThresholdSeverity, ostream* LogStream>
template<>
template <class T>
ErrorLogger<ThresholdSeverity, LogStream>::Log<ThresholdSeverity+1>&
ErrorLogger<ThresholdSeverity,
LogStream>::Log<ThresholdSeverity+1>::operator<<
(
ErrorLogger<ThresholdSeverity, LogStream>::Log<Severity>& Stream,
const T& Object
)
{
return *this;
};

//
// sample usage
//

typedef ErrorLogger<25, &cout> ErrorLog;

int main()
{
ErrorLog::Log<10>() << "Severe error! Eject Immediately!" << endl;
ErrorLog::Log<25>() << "Less sever error - no need to panic just yet!" <<
endl;
ErrorLog::Log<50>() << "Minor error - dont bother logging this." << endl;
};

\end{code}

Instead of having a pointer to a ostream as a template parameter, a
singleton object could be used instead.
Unfortunately, this does not include the __FILE__ and __LINE__ information
that the original poster required. But a simple macro would suffice,
something like

#define SWFault(ErrorLogClass, Severity, Message) \
ErrorLogClass::Log<Severity>() << "Something died at line " \
<< __LINE << " in file " << __FILE__ << " with severity: " Severity << ' '
<< Message << endl;

With a typical usage being

SWFault(ErrorLog, 1, "Ackk! The computer ate my document! (doc number:" <<
DocumentNumber << ')' )

The point being that you do not need to format the string separately before
calling the macro - the line
"Ackk! The computer ate my document! (doc number:" << DocumentNumber <<
')'
its itself a valid macro parameter.

Again, apologies in advance for any coding errors. But hopefully you get
the basic idea:-)

Cheers,
Ian McCulloch

0 new messages