I stumbled upon an informative article: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ which pointed out a great number of problems that exist in my current suite of debugging macros.
The full code for the final version of the macro is given near the end of the article if you follow the link.
The general form as presented is like this (somebody please correct me if i am wrong in transposing it):
#ifdef DEBUG
#define ASSERT(cond) \
do \
{ \
if (!(cond)) \
{ \
ReportFailure(#cond, __FILE__, __LINE__, 0); \
HALT(); \
} \
} while(0)
#else
#define ASSERT(cond) \
do { (void)sizeof(cond); } while(0)
While thinking about modifying my code with what I have learned, I noticed a couple of interesting variations posted in the comments for that article:
One was that you cannot use this macro with the ternary operator (i.e. cond?ASSERT(x):func()
), and the suggestion was to replace the if()
with the ternary operator and some parentheses as well as the comma operator. Later on another commenter provided this:
#ifdef DEBUG
#define ASSERT(x) ((void)(!(x) && assert_handler(#x, __FILE__, __LINE__) && (HALT(), 1)))
#else
#define ASSERT(x) ((void)sizeof(x))
#endif
I thought the use of logical and &&
is particularly smart in this case and it seems to me that this version is more flexible than one using the if
or even the ternary ?:
. Even nicer is that the return value of assert_handler
can be used to determine if the program should halt. Although I am not sure why it is (HALT(), 1)
instead of just HALT()
.
Are there any particular shortcomings with the second version here that I have overlooked? It does away with the do{ } while(0)
wrapped around the macros but it seems to be unnecessary here because we don't need to deal with if
s.
What do you think?
sizeof
is not applicable to functions and elements of bit-fields. As suggested here #4031459, you can use((void)(true ? 0 : ((x), void(), 0)));
instead. – Siusan