assert
is a macro (it has to be one, to give __LINE__
and __FILE__
information).
You could define your own. I would name it something else like tassert
for readability reasons, perhaps like (untested code)
#ifdef NDEBUG
#define tassert(Cond) do {if (0 && (Cond)) {}; } while(0)
#else
#define tassert_at(Cond,Fil,Lin) do { if ((Cond)) { \
time_t now##Lin = time(NULL); \
char tbuf##Lin [64]; struct tm tm##Lin; \
localtime_r(&now##Lin, &tm##Lin); \
strftime (tbuf##Lin, sizeof(tbuf##Lin), \
"%Y-%m-%d,%T", &tm##Lin); \
fprintf(stderr, "tassert %s failure: %s %s:%d\n", \
#Cond, tbuf##Lin, Fil, Lin); \
abort(); }} while(0)
#define tassert(Cond) tassert_at(Cond,__FILE__,__LINE__)
#endif /*NDEBUG*/
I am using cpp concatenation ##
with Lin
to lower probability of name collisions, and I am using cpp stringification #
to make a string out of Cond
macro formal. The Cond
is always expanded, to make sure the compiler catch syntactic errors in it even when disabling tassert
with NDEBUG
as assert(3) does.
One could put most of the code in the above macro in some function, e.g.
void tassert_at_failure (const char* cond, const char* fil, int lin) {
timer_t now = time(NULL);
char tbuf[64]; struct tm tm;
localtime_r (&now, &tm);
strftime (tbuf, sizeof(tbuf), "%Y-%m-%d,%T", &tm);
fprintf (stderr, "tassert %s failure: %s %s:%d\n",
cond, tbuf, fil, lin);
abort();
}
and then just define (a bit like <assert.h>
does...)
#define tassert_at(Cond,Fil,Lin) do { if ((Cond)) { \
tassert_at_failure(#Cond, Fil, Lin); }} while(0)
but I don't like much that approach, because for debugging with gdb
having abort()
being called in the macro is much easier (IMHO size of code for debugging executables does not matter at all; calling abort
in a macro is much more convenient inside gdb
- making shorter backtraces and avoiding one down
command...). If you don't want libc
portability and just use recent GNU libc
you could simply redefine the Glibc specific __assert_fail
function (see inside <assert.h>
header file). YMMV.
BTW, in real C++ code I prefer to use <<
for assertion-like debug outputs. This enables usage of my own operator <<
outputting routines (if you give it as an additional macro argument) so I am thinking of (untested code!)
#define tassert_message_at(Cond,Out,Fil,Lin) \
do { if ((Cond)) { \
time_t now##Lin = time(NULL); \
char tbuf##Lin [64]; struct tm tm##Lin; \
localtime_r(&now##Lin, &tm##Lin); \
strftime (tbuf##Lin, sizeof(tbuf##Lin), \
"%Y-%m-%d,%T", &tm##Lin); \
std::clog << "assert " << #Cond << " failed " \
tbuf##Lin << " " << Fil << ":" << Lin \
<< Out << std::endl; \
abort (); } } while(0)
#define tassert_message(Cond,Out) \
tassert_message_at(Cond,Out,__FILE__,__LINE__)
and then I would use tassert_message(i>5,"i=" << i);
BTW, you might want to use syslog(3) instead of fprintf
in your tassert_at
macro.