Preface
I know that there are several libraries for auto-testing available. Let's ignore that for this question, please.
Motivation
Implementing some library I got tired of manual testing, so I started to write a "self-test" program, starting with code using many assert()
s.
Unfortunately when an assert()
fails, only limited information is shown on the screen, and I would typically have to use the debugger to examine the core dump to get more details of the failure.
So I added a macro that allows a printf()
-like output (implemented via the E()
(for error) macro) when an assertion fails; I named that macro VA()
(for verbose assertion):
#define VA(assert_cond, msg, ...) do { \
if ( !(assert_cond) ) E(msg, ##__VA_ARGS__); \
assert(assert_cond); \
} while (0)
Using that would look like this:
VA(FASTWORD(FASTWORD_BITS - 1) == 0, "%s: FASTWORD() failed", __func__);
As the self-test program used array-like data structures, I needed to inspact those as well, so I output those before doing the tests, resulting in a lot of output even when all tests succeed.
So I invented another macro, VFA()
(verbose failed assertion) that uses a "lambda parameter" like this:
#define VFA(assert_cond, cmd, msg, ...) do { \
if ( !(assert_cond) ) { \
E(msg, ##__VA_ARGS__); \
cmd; \
} \
assert(assert_cond); \
} while (0)
While writing that I wondered how the preprocessor would parse commata for a use case like this:
VFA(fw[0] == out_fw0 && fw[1] == out_fw1,
dump_fastwords_range(fw, 4, pos, (pos + count) % FASTWORD_BITS),
"%s: __clear_fw_bits_up(%d, %d) failed", context, pos, count);
I mean it could be possible that the condition could be the first parameter, dump_fastwords_range(fw
could be the second, 4
could be the third, and so on...
However that is not the case with gcc
at least.
The other thing is cmd;
in the macro:
My first version did not include the semicolon, so I would have to write (which looks really ugly):
VFA(fw[0] == out_fw0 && fw[1] == out_fw1,
dump_fastwords_range(fw, 4, pos, (pos + count) % FASTWORD_BITS);,
"%s: __clear_fw_bits_up(%d, %d) failed", context, pos, count);
OK, here's another use example of my macro:
VFA(fw[0] == out_fw0 && fw[1] == out_fw1,
{
const unsigned first = pos >= count ?
pos - count : FASTWORD_BITS + pos - count + 1;
dump_fastwords_range(fw, 4, first, pos);
},
"%s: __clear_fw_bits_dn(%d, %d) failed", context, pos, count);
Questions
The questions I have are:
- Is parsing of the macro parameters portable across compilers?
- Will the
cmd
use create any trouble, considering the parameter could be rather complex (as the last example suggests)?