I'm using a unit test framework that relies on a REQUIRE
macro for performing assertions.
Simplified, the macro works like this:
#define REQUIRE( expr ) INTERNAL_REQUIRE( expr, "REQUIRE" )
Which is defined similar to this:
#define INTERNAL_REQUIRE( expr, macroName ) \
PerformAssertion( macroName, #expr, expr );
PerformAssertion
's first two parameters are of the type: const char*
. The reason for the second parameter (#expr
) is so the exact expression that was asserted can be logged. This is where the issue lies. The preprocessor expands the expression before it is passed as a const char *
, so it's not the same expression that was originally asserted.
For instance:
REQUIRE( foo != NULL );
Would result in this call:
PerformAssertion( "REQUIRE", "foo != 0", foo != 0 );
As you can see, the expression is partially expanded, e.g. the expression foo != NULL
appears in the log as foo != 0
. The NULL
(which is a macro defined to be 0
) was expanded by the C preprocessor before building the assertions message text. Is there a way I can ignore or bypass the expansion for the message text?
EDIT: Here's the solution, for anyone curious:
#define REQUIRE( expr ) INTERNAL_REQUIRE( expr, #expr, "REQUIRE" )
#define INTERNAL_REQUIRE( expr, exprString, macroName ) \
PerformAssertion( macroName, exprString, expr );
Try making the stringifying before the call to the internal require. Your problem is that it is passed to internal require in the second expansion which expands NULL. If you make the stringifying happen before that, e.g. In the require macro, it will not expand the NULL.
Here is what's going on: since you macro where the "stringization" operator #
is applied is second-level, the sequence of operations works as follows:
REQUIRE(NULL)
and performs argument substitution as per C 6.10.3.1. At this point, the replacement looks like INTERNAL_REQUIRE( 0, "REQUIRE" )
, because NULL
is expanded as 0
.INTERNAL_REQUIRE
; at this point, the fact that the macro has been called with NULL
is lost: as far as the preprocessor is concerned, the expression passed to INTERNAL_REQUIRE
is 0
.A key to solving this problem is in this paragraph from the standard:
A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded.
This means that if you would like to capture the exact expression, you need to do it in the very first level of the macro expansion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With