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
Related
I'm working with WPP (Windows Tracing) and the basic macro looks like this:
trace(SEVERITY, SOURCE, "%s started", app_name);
I would like to do is something like this but I get a variety of compiler/trace errors:
#define trace_with_console(__level, __flags, __format, ...) \
do \
{ \
printf(__format, __VA_ARGS__); \
trace(__level, __flags, __format, ...); \
} while (false);
Is there a correct way to call the trace macro from the trace_with_console macro?
EDIT: Thank you everyone for replying. The problem isn't a macro issue, it is the WPP for Windows trace processor failing.
This tool parses the trace macro call and extracts the text to keep it secret, and replaces it with a GUID. You need debugging information to read the traces.
The processor gets confused by the trace macro being called within this context. At this stage it doesn't seem possible to do what I'm trying to do. #jxh answer works well, just not with WPP.
Thank you all again!
Without knowing anything about the trace() macro or function other than how you are trying to invoke it, I would rewrite your proposed macro as:
#define trace_with_console(__level, __flags, __format, ...) \
do \
{ \
printf(__format, __VA_ARGS__); \
trace(__level, __flags, __format, __VA_ARGS__); \
} while (false)
Replace the ... in the call to trace with __VA_ARGS__ instead. The contents of the variable arguments represented by ... in your arg list for trace_with_console will be used in the expansion of __VA_ARGS__.
Remove the ; at the last line of your macro. This allows your macro to be treated as a statement that requires a ; at the end.
One disadvantage with the macro as it is written is that there are arguments that will be evaluated twice. Once evaluation will happen in the call to printf, and another evaluation in the call to trace.
Since __format and __VA_ARGS__ are being passed into printf, we assume trace can accept the same format specifications and corresponding arguments. Then, if there is a reasonable max size for your trace message, you may avoid multiple argument evaluation by using snprintf.
#define trace_with_console(__level, __flags, __format, ...) \
do \
{ \
char __buf_for_trace[BUF_FOR_TRACE_MAX_SIZE]; \
snprintf(__buf_for_trace, sizeof(__buf_for_trace), \
__format, __VA_ARGS__); \
printf("%s", __buf_for_trace); \
trace(__level, __flags, "%s", __buf_for_trace); \
} while (false)
In C++, you can remove the macro and use a variadic template function instead.
template <typename LevelType, typename FlagsType, typename... Types>
void trace_with_console (
LevelType level, FlagsType flags,
const char *format, Type... args) {
printf(format, args...);
trace(level, flags, format, args...);
}
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; #
}
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.
I want use the NAME of the macro inside the macro expansion as string.
Example:
#define DEBUG_TEST(a, ...) printf("DEBUG_TEST::%s (%d) : " a, __FUNCTION__, __LINE__, ##__VA_ARGS__)
Want to change to something that doesn't need the "DEBUG_TEST" anymore in the exapansion, but something like:
#define DEBUG_TEST(a, ...) printf(__MACRO__ "::%s (%d) : " a, __FUNCTION__, __LINE__, ##__VA_ARGS__)
Of course in my example __MACRO__ dosen't exist...
I believe this is not possible. My suggestion would be to create another macro that takes the name of the macro as argument. Then use this macro in your other macroses.