Implementing an assertion macro I stumbled upon a problem on Xcode clang.
I use the following code:
void Log(LogLevel logLevel, const std::string& message);
std::string FormatString(const char* format, ...);
#define LOG(Level, Format, ...) \
{ \
std::string _ac_err_str_ = FormatString(Format, ##__VA_ARGS__); \
Log(Level, _ac_err_str_); \
}
#define checkf(expr, Format, ...) { if(!(expr)) { LOG(LL_ErrorMessage, TXT("Log in file ") __FILE__ TXT(" at line %d: ") TXT(#expr) TXT(". ") Format, __LINE__, __VA_ARGS__); } }
Everything works fine with MSVC but on Clang I get the following error: Expected expression
I've tracked down the problem on this line:
std::string _ac_err_str_ = FormatString(Format, ##__VA_ARGS__);
More specifically the problem is on the ##__VA_ARGS__ part because if I remove it everything compiles.
Also, if I pass a third parameter to the macro it compiles, e.g.:
checkf(false, "Error message"); // Won't compile
checkf(false, "Error %s", "message"); // Will compile
It seems like there's an error on passing the macro's variadic arguments to FormatString, but I can't find out the reason or how to fix this.
Apparently on Clang I need to change my checkf macro like this
#define checkf(expr, Format, ...) { if(!(expr)) { LOG(LL_ErrorMessage, TXT("Log in file ") __FILE__ TXT(" at line %d: ") TXT(#expr) TXT(". ") Format, __LINE__, ##__VA_ARGS__); } }
Notice that I've used ##__VA_ARGS__ instead of just __VA_ARGS__.
It compiles with both MSVC and Clang as well.
Related
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:
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/logical/bool.hpp>
#include <boost/preprocessor/cat.hpp>
#define MyAssert(...) BOOST_PP_CAT(MY_ASSERT,BOOST_PP_BOOL(BOOST_PP_SUB(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), 1)))(__VA_ARGS__)
#define MY_ASSERT0(expr) MY_ASSERT1(expr,)
#define MY_ASSERT1(expression, ...) \
do { \
if(!(expression)) \
{ \
std::printf("Assertion error: " #expression " | " __VA_ARGS__); \
std::abort(); \
} \
} while(0)
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 token MY_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 with MY_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:
#define HAS_ARGS(...) HAS_ARGS_(__VA_ARGS__,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,)
#define HAS_ARGS_(a1,a2,a3,a4,a5,b1,b2,b3,b4,b5,c1,c2,c3,c4,c5,d1,d2,d3,d4,d5,e, N, ...) N
It's argument counting, but with a twist. When __VA_ARGS__ is a single argument (no extra ones), the N 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:
#define MyAssert(...) CONCAT(MY_ASSERT, HAS_ARGS(__VA_ARGS__))(__VA_ARGS__)
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:
#define VA_ARGS_HEAD(N, ...) N
#define VA_ARGS_HEAD_STR(N, ...) #N
Note that in usage, in order to not get warnings, you should do VA_ARGS_HEAD(__VA_ARGS__, ) (with the extra ,) so that VA_ARGS_HEAD is never used with a single parameter (trick taken from StoryTeller's answer).
We define the following helper function:
#include <stdarg.h>
#include <stdio.h>
inline int assertionMessage(bool, const char *fmt, ...)
{
int r;
va_list ap;
va_start(ap, fmt);
r = vprintf(fmt, ap);
va_end(ap);
return r;
}
When the assertion has a format string, the function would work with __VA_ARGS__ as is, however when the bool 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:
#define MyAssert(...) \
do { \
if(!(VA_ARGS_HEAD(__VA_ARGS__, ))) \
{ \
printf("Assertion error: %s | ", VA_ARGS_HEAD_STR(__VA_ARGS__, )); \
assertionMessage(__VA_ARGS__, ""); \
abort(); \
} \
} while(0)
Note that assertionMessage doesn't have printf 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:
#define MyAssert(expression, msg) \
do { \
if(!(expression)) \
{ \
std::cerr << msg; \
abort(); \
} \
} while(0)
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:
MyAssert(true, "message " << variable << " units");
I think the optionality is bogus here, as you are outputting "Assertion error:" meaning that you expect a message.
Here is a macro definition:
#define print_debug(fmt, ...) \
__print_debug__(__FILE__, __func__, __LINE__, fmt, ## __VA_ARGS__)
I cannot locate the expansion of __FILE__,__func__ ,__LINE__ and ## __VA_ARGS__ in visual studio(I can get __VA_ARGS__ that corresponds to a variable arguments). In particular what dose ## __VA_ARGS__ mean?
__print_debug__ is a function like macro which will print all arguments passed to it.
Here
__FILE__ :--> will pass current File name
__func__:--> will pass current function name.
__LINE__ :--> will pass current line number
You can have a look at This Link for more information on it.
Now fmt and ## __VA_ARGS__ :
Let me take below example.
print_debug(fmt,var1,var2,var3)
the macro expansion will be as following:
__print_debug__(__FILE__, __func__, __LINE__, fmt,var1,var2,var3)
This is because ## will concatenate all the the variable list of argument i.e. __VA_ARGS__
Save following code in macro.c file and compile it with
gcc -E macro.c -o macro.i
command and have a look at the macro.i file.
#define print_debug(fmt, ...) \
__print_debug__(__FILE__, __func__, __LINE__, fmt, ## __VA_ARGS__)
void call_Macro()
{
print_debug(2,3,4,5) //Here 2 is nothing but fmt
}
int main()
{
call_Macro();
return 0; #
}
I am trying to write a debug macro in the common subset of C and C++. This is what I've come up with so far:
#define OUTPUT_ERROR(...) printf("%s(%d) : %s() : %s\n", __FILE__, __LINE__, __func__, #__VA_ARGS__)
Unfortunately, I cannot figure out how to give it variables to output. Is there any way to force a variable to be expanded at runtime? For example:
OUTPUT_ERROR("%s was broken", my_var);
or simply in place
OUTPUT_ERROR(my_var + "some text");
It seems to me that you're trying to cram too much into one printf() call. You don't need to do that. In particular, to support usage such as your first example:
OUTPUT_ERROR("%s was broken", my_var);
you could easily split the output over multiple printf() calls with a macro like this:
#define OUTPUT_ERROR(...) do { \
printf("%s(%d) : %s() : ", __FILE__, __LINE__, __func__); \
printf(__VA_ARGS__); \
putchar('\n'); \
} while (0)
I think you're looking to define a macro prepending some debugging information to your formatted log.
To achieve this, you can use the following macro :
#define LOG(format, ...) \
printf("%s(%d) : %s() " format, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
This way LOG("%d", 123) will expand to printf("%s(%d) : %s() " "%s", __FILE__, __LINE__, __FUNCTION__, 123).
As consecutive string literals are merged ("a" "b" is equivalent to "ab"), the printf format is correctly built.
Also note the use of , ## __VA_ARGS__. This is a GNU extension (see https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html) that allows empty variadic arguments.
This technique will however fail if format is not a string literal.
See an example here : http://ideone.com/gMnaES
There is a debug class that is used to print out debug statements based on a level. The higher the level, the more detailed messages you would get. The problem occurs when I am trying to do print out a uint64_t. On different platforms using printf style it could be either %li or %lli. I want it to become portable. However, based on this, I could include a PRId64 and it would adjust accordingly. The problem is because my messages are customized, it takes in a const char *. I am unable to do with with a message such as "Calling based on pts %"PRId64, mv.pts because it is expecting a char * where there isn't one. What are other approaches in solving this?
Inside the main code
#include "debug.h"
#include <inttypes.h>
#define P_DBG(level, format, ...) DBGPRINT(level, ("%-5d%s::%s() - " format "\n", __LINE__, str().c_str (), __FUNCTION__, __VA_ARGS__))
#define P_ALL(format, ...) P_DBG(P_ALWAYS, format, __VA_ARGS__)
class Something
{
Something::Print(uint64_t a) {
P_ALL("Calling based on %"PRId64 "\n", a)
}
Inside debug.h
class vdebugPrint {
class vdebugPrint(int ID, int level) : v_id(ID), v_level(level) {}
int Print(const char* formatString, ...) __attribute__((__format(printf,2,3)));
}
...
#define VPRINT3(level, msg) VdebugPrint(ID, level).Print msg
#define PRINT(level, msg) VPRINT##level(level,msg)
#define DBGPRINT(level, msg) PRINT(level,msg)
...
Inside debug.cpp
...
VdebugPrint::Print(const char* format, ...)
{
va_list argumentList;
va_start(argumentList, formatString);
vprintf(formatString, argumentList);
va_end(argumentList);
}
The exact error message is
error: expected ‘)’ before ‘PRId64’
AV_TRACE("Calling based on %"PRId64"\n", mv.pts);
^
/.../debug.h:140:94: note: in definition of macro ‘VPRINT3’
#define VPRINT3(level, msg) VdebugPrint(ID, level).DebugPrint msg
/.../debug.h:146:45: note: in expansion of macro ‘DBGPRINT’
#define DBGPRINT(level, msg) PRINT(level, msg)
^
/.../file.h:54:41: note: in expansion of macro ‘DBGPRINT’
#define P_PBD(level, format, args...) DBGPRINT(level, ("%-5d%s::%s() - " format "\n", __LINE__, str().c_str(), __FUNCTION__, ##args))
EDIT:
I've attempted Olaf suggestion to no avail. Here is what I've done. Everything else is the same except for the main code.
...
class Something
{
Something::Print(uint64_t a) {
const char* message = "Calling based on %";
size_t mlen = strlen(message);
char buf[mlen + strlen(PRId64) + 1];
strcpy(buf, message);
strcpy(buf + mlen, PRId64);
P_ALL(buf, a);
}
}
The error that I get is
error: ‘PRId64’ was not declared in this scope
char buf[mlen + strlen(PRId64) + 1];
^
EDIT2: So I fixed the problem with it not finding PRId64. I had to define __STDC_FORMAT_MACROS from here before include inttypes. This now solves the problem and it compiles!
The macros defined in inttypes.h are string literals with the proper printf type specifier. So you have to append this to the format string. Normal usage would be to use string literal concatenation.
If your format string is variable, you have to append the specifier to the string:
char buf[strlen[format] + strlen[PRId64] + 1];
strcpy(buff, format);
strcat(buff, PRId64);
Note this could be optimized using strcpy for both:
size_t flen = strlen(format);
char buf[flen + strlen[PRId64] + 1];
strcpy(buff, format);
strcpy(buff + flen, PRId64);
Caution: The format string has to be NUL-terminated!
Update:
The original answer was for C as that was one of the language-tags and the code looked more like C than C++. However, according to comments, you can use this also for C++11 (check your compiler's support), if you #define __STDC_FORMAT_MACROS before #include <inttypes.h>. (Note the include might appear in other headers, so you should define the macro as early as possibly in your file.)
You need spaces between the adjacent string literals and the macros for the expansion and concatenation to work. For example:
// Note the space on either side of PRId64
AV_TRACE("Calling based on %" PRId64 "\n", mv.pts);
(I don't know if this is required according to the standard, but it is required to make it work for some preprocessors I've used.)
I have a task at work to make the static library more secure , in order to do so I need to replace the formatted strings of printf for them to appear differently in the compiled static library , for this to happen it must be done in the pre processor stage .
What I did (and it actually works in visual studio) is the following ( and it is just a pseudo example ):
char * my_array[] = {"abcd", "a %d", " b %d %s "};
#define GENERIC_ARRAY(x) my_array[x]
#define VARIADIC_DEBUG_PRINT(...) DebugPrintFunction (__FILE__, __LINE__, __func__, __VA_ARGS__)
#define PRINT_BY_LEVEL(x) VARIADIC_DEBUG_PRINT x
#define REPLACE_STRING(x,...) PRINT_BY_LEVEL((GENERAL_LEVEL,GENERIC_ARRAY(__COUNTER__),__VA_ARGS__))
#define MY_PRINTF(x,...) REPLACE_STRING((void*)0,(void*)0,__VA_ARGS__)
All of this overhead is for me to trick the compiler to accept prints without any arguments except the string
So when testing it in my main.c I tried the following And it worked :
MY_PRINTF("Hello World");
MY_PRINTF("My Val %d", i);
MY_PRINTF("MY VAL %d My String %s", i, s);
But when switching to GCC, he does not like the format of the first print i.e :
MY_PRINTF("Hello World");
And throws me an compilation error :
error: expected expression before ')' token
Any ideas how may I trick the compiler and accept it ? or maybe better ideas how to rename the string safely after compilation ?
You can try something like :
#include <stdio.h>
#define PRINT(x, ...) printf(x, ##__VA_ARGS__)
int main (int argc, char *argv[]) {
PRINT("Hello\n");
PRINT("World %d\n", 42);
return 0;
}
It works with gcc 4.8 (not tried with earlier version, but it should work too)
Using ##__VA_ARGS__, you may try:
#define MY_PRINTF(x, ...) \
VARIADIC_DEBUG_PRINT(GENERAL_LEVEL, GENERIC_ARRAY(__COUNTER__), (void*)0, (void*)0, ##__VA_ARGS__)