Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom `assert` macro that supports commas and error message

I would like to create a custom version of the assert macro defined in <cassert>, that displays an error message when the assertion fails.


Desired usage:

custom_assert(AClass<T1, T2>::aBoolMethod(), "aBoolMethod must be true");


Flawed test implementations:

#define custom_assert(mCondition, mMessage) ...
// This fails because mCondition may have commas in it

#define custom_assert(..., mMessage)
// Not sure about this either - mMessage may be an expression containing commas
// as well

How can I correctly implement a custom assert that takes a boolean expression (with possible commas) as the first argument and a string expression (with possible commas) as the second argument?

Or is there a way to implement assertions without the use of macros?

like image 325
Vittorio Romeo Avatar asked Feb 22 '14 17:02

Vittorio Romeo


2 Answers

You were quite close, what you need to use is simply this:

#define myAssert(message, ...) do { \
    if(!(__VA_ARGS__)) { \
        /*error code*/ \
    } \
} while(0)

The special preprocessor variable __VA_ARGS__ will expand to whatever was passed in the place of the three dots, including all comas.

Note that the preprocessor will not interprete commas in the condition in the very least, it will just paste them as is into the if() statement. Which is precisely what you want if you want to pass templated conditions, as hinted by the comments.

Commas in the message string are not a problem either, since the preprocessor understands about string literals and does not interprete anything within the double quotes.

like image 122
cmaster - reinstate monica Avatar answered Sep 30 '22 13:09

cmaster - reinstate monica


The straightforward

assert(AClass<T1, T2>::aBoolMethod() && "aBoolMethod must be true");

fails:

error: macro "assert" passed 2 arguments, but takes just 1

but if you add an extra pair of parenthesis around the first argument, it works. Like this:

#include <cassert>

template <typename A, typename B>
struct C { 
  bool f() { return false; }
};

int main() {
  assert((C<int,int>().f()) && "some message, with comma");
  //     ^                ^
}

Note: it was also pointed out by Adam Rosenfield in a comment.

Perhaps the only benefit over the __VA_ARGS__ approach is that it doesn't dump yet another macro on the user. If you forget the parenthesis, you can get a compile time error, so I see it as a safe solution.

like image 30
Ali Avatar answered Sep 30 '22 13:09

Ali