What's the way to implement a standard-compliant assert macro with an optional formatted message?
What I have works in clang, but (correctly) triggers the -Wgnu-zero-variadic-macro-arguments
warning if it is turned on (e.g. via -Wpedantic
) when the macro is used without the optional message. Wandbox
#define MyAssert(expression, ...) \
do { \
if(!(expression)) \
{ \
printf("Assertion error: " #expression " | " __VA_ARGS__); \
abort(); \
} \
} while(0)
One needs to really use the preprocessor to the max in order to differentiate no additional arguments from the case where they are present. But with Boost.PP one can do this:
MyAssert
must accept at least one argument (standard). Then we count the arguments, subtract one, and turn to a boolean (0 or 1). This 0 or 1 is concatenated to the tokenMY_ASSERT
to form a macro name, to which we proceed to forward the arguments.MY_ASSERT1
(with args), is your original macro.MY_ASSERT0
substitutes itself withMY_ASSERT1(expr,)
, the trailing comma means we pass another argument (thus fulfilling the requirement for the one extra argument), but it is an empty token sequence, so it does nothing.You can see it live.
Since we already went down this rabbit hole, if one doesn't want to pull in Boost.PP the above can be accomplished with the usual argument counting trick, slightly adapted. First, we must decide on a maximum limit for the arguments we allow. I chose 20, you can choose more. We'll need the typical
CONCAT
macro, and this macro here:It's argument counting, but with a twist. When
__VA_ARGS__
is a single argument (no extra ones), theN
resolved as 0. Otherwise, it is resolved as 1. There can be up to 20 extra arguments after the expression, any number of which will resolve to the same 1. Now we just plug it into the same place we used boost before:You can tinker with it here
I have a solution which I'm not particularly proud of..
We can obtain the first argument in plain form and as a string using:
Note that in usage, in order to not get warnings, you should do
VA_ARGS_HEAD(__VA_ARGS__, )
(with the extra,
) so thatVA_ARGS_HEAD
is never used with a single parameter (trick taken from StoryTeller's answer).We define the following helper function:
When the assertion has a format string, the function would work with
__VA_ARGS__
as is, however when thebool
is the only argument, we're missing a format string. That's why we'll add another empty string after__VA_ARGS__
when invoking it:Note that
assertionMessage
doesn't haveprintf
in its name. This is deliberate and intended to avoid the compiler giving format-string related warnings for its invocations with the extra""
argument. The down-side for this is that we don't get the format-string related warnings when they are helpful.The basic solution is to use
<<
on cerr:This solution uses C++ streams, so you can format the output as you see fit. Actually this is a simplification of a C++17 solution that I'm using to avoid temporaries (people tend to use
+
instead of<<
with this solution, triggering some efficiency warnings).Use it then like this:
I think the optionality is bogus here, as you are outputting "Assertion error:" meaning that you expect a message.