I am trying to write a macro dbgassert
similar to the standard assert
. In addition to what assert
does, I want to dbgassert
You're assuming that assert
is implemented by calling __assert
. This may well be how one particular implementation works, but certainly cannot be depended on in general.
Instead, follow the documentation: test your condition and on failure emit diagnostic information to standard error, then call std::abort
.
Just as a note, I was able to figure out another means to solve the trailing comma issue, in addition to @cmaster and @ds27680's solution. Since having __VA_ARGS__
leads to an extra comma, I can pack __VA_ARGS__
into a std::tuple
or a function call, and use the tuple/result as a parameter of the real function. Now empty __VA_ARGS__
's won't be a problem because its packed into one valid value (i.e. either an empty tuple or the return value of a nullary function). I guess this is longer in code, but a bit more portable without involving ##
.
The above two cases are shown in the dbgassert
and dbgassert1
macros in the code below, respectively.
#include <cassert>
#include <iostream>
#include <tuple>
using namespace std;
template <typename ...Args>
string print_tuple(tuple<Args...> tp) {
return ""; //print the tuple...
}
template <typename ...Args>
void realdbgassert(tuple<Args...> info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << print_tuple(info) << endl;
std::abort();
}
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (std::tie(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
void realdbgassert1(string info,const char *msg, const char *file, int line) {
cout << "Assertion failed! \nFile " << file << ", Line " << line << endl
<< " Expression: " << msg << endl
<< " Info: " << info << endl;
std::abort();
}
template <typename ...Args>
string print_info(Args ... args) {
return ""; //print stuff
}
#define dbgassert1(EX,...) \
(void)((EX) || (realdbgassert1 (print_info(__VA_ARGS__),#EX,__FILE__, __LINE__),0))
int main() {
dbgassert(1>2,"right","yes"); //OK
dbgassert(1>2,"right"); //OK
dbgassert(1>2); //OK now
dbgassert1(1>2); //OK too
}
Your problem is the value of __VA_ARGS__
which is empty in the problem case. So, when the preprocessor expands realdbgassert(#EX, __FILE__, __LINE__, __VA_ARGS__)
, the result is an unfinished parameter list realdbgassert("1>2", "foo.c", 42, )
. Note that the parameter list is not correctly terminated due to the empty expansion of __VA_ARGS__
.
To fix this, you need to use some kind of trick. The best solution is, to tweak the circumstances so that __VA_ARGS__
includes the last unconditional argument, and pass that together with the optional ones at the end of the function call. This is best, because it's standard C.
The other fix that I know of, is a gcc extension to the language: See this gcc documentation page for more detailed info, but you can fix your macro by adding a double ##
in front of __VA_ARGS__
:
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ## __VA_ARGS__),0))
Ps:
The #
is the stringification operator of the preprocessor: it turns the value of the macro parameter into a string literal, i. e. instead of pasting 1>2
it pastes "1>2"
.
You have to write the contents of the function __assert
- if you specified it's extern
you should attach the file that contains the function's definition to the compilation process. If you don't know how to write a multiple-file program, I can't really help you.
Place the token pasting operator (##) before __VA_ARGS__
. This will have as effect deleting of the comma before __VA_ARGS__
if __VA_ARGS__
is empty.
Your macro will be then:
#define dbgassert(EX,...) \
(void)((EX) || (realdbgassert (#EX, __FILE__, __LINE__, ##__VA_ARGS__),0))
Please note that the token pasting is an GNU CPP extension as one of the other posters correctly mentioned (see https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html).
The MS compiler (tested under VS2010) does not need the token pasting, it simply removes the trailing comma if __VA_ARGS__
is empty see: http://msdn.microsoft.com/en-us/library/ms177415(v=vs.110).aspx