C logging with variadic macros - c

I'm trying to write an overhead-free logging macro in C.
My first idea is:
#define debug_print(...) \
{ \
printf(_LOG_FMT, _LOG_ARGS); \
printf(__VA_ARGS__); \
printf("\n"); \
}
But this has the problem that I have to call printf three times (overhead).
My second idea is:
#define _LOGFUNCTION(LEVEL, message, ...) \
printf(_LOG_FMT message "\n", _LOG_ARGS, __VA_ARGS__)
But now I can't pass it a simple string to log. It'll complain about zero arguments in VA_ARGS.
Is there a way to fix both of these problems?
Thanks a ton for your help!

Found the answer form the comment above!
#define BAR_HELPER(fmt, ...) printf(fmt "\n%s", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, "")
https://stackoverflow.com/a/8673872/5531233
Thanks again Some programmer dude!

Related

Debug print by macro with variable names

Is there a way, how to neatly pass variable name and its value to a debug macro? What I'm using now is "just" this.
#if DEBUG_LEVEL >= 1
#define DEBUG_PRINT(fmt, ...) do{\
fprintf(stderr, ANSI_COLOR_YELLOW "[DEBUG]: %s:%c\t", __FILE__, __LINE__);\
fprintf(stderr, fmt ANSI_COLOR_RESET, __VA_ARGS__);}while(0)
#else
#define DEBUG_PRINT(...)
#endif
#define STR(x) #x
It's by all means fine, but when I want to output what variable belongs to which name, I use construct like this and it easily gets too cumbersome.
DEBUG_PRINT(STR(min_a)": %u\t"STR(max_a)": %u\t"STR(min_c)": %u\t"STR(max_c)":%u\t"STR(min_m)": %u\t"STR(max_m)":%u\t"STR(d)": %d\n", min_a, max_a, min_c, max_c, min_m, max_m, d);
Is there a way, how to get rid of those STR(x) in DEBUG_PRINT in such a way, that variable name stays visible and the overall formatting stays pretty much the same? I don't mind putting in the format specifiers, though my first thought was to get rid of those as well, if possible. The most important thing is to retain variable number of arguments.
So my question is, how to write a macro, that do this "pretty printing", without the need to call STR(x) - something like this: DEBUG_PRINT_VARS(min_a, max_a, min_c, max_c, min_m, max_m, d)
Well, I sort of solved it for now by using MAP macro (https://github.com/swansontec/map-macro), edited it for allowing two argument function, and then having:
#if DEBUG_LEVEL >= 1
#define DEBUG_PRINT(...) do{\
fprintf(stderr, ANSI_COLOR_YELLOW "[DEBUG]: %s:%d\t", __FILE__, __LINE__);\
MAP(PRINT_ARG, __VA_ARGS__); \
fprintf(stderr, "\n" ANSI_COLOR_RESET);}while(0)
#else
#define DEBUG_PRINT(...)
#endif
#define PRINT_ARG(x, y) fprintf(stderr, #x": "y"\t", x);
and using it like this
DEBUG_PRINT(min_a, "%u", max_a, "%u", min_c, "%u", max_c, "%u", min_m, "%u", max_m, "%u", d, "%d");
It answers the question itself, so that's the reason why am I posting it as an answer, though I'd be really interested in finding out a way, how to produce conversion specifiers implicitly

How to modify the argument of a multi-argument macro?

I have various kinds of printf macros in my code defined along those lines:
#define DEBUG(...) printf(__VA_ARGS__)
This works well:
DEBUG("Hello %d",1);
will be the same as
printf("Hello %d",1);
Now can I make my macro also edit the args that are passed in to, say, add a \n at the end of the first argument? I.e. so that
DEBUG("Hello %d",1);
turns into
printf("Hello %d\n",1);
I suggest using:
#define DEBUG(fmt, ...) printf(fmt "\n", __VA_ARGS__)
The drawback is that you have to have at least one non-format string argument, i.e. you cannot use the macro as:
DEBUG("foo");
anymore.
For some compilers there are work-arounds allowing empty __VA_ARGS__ like
#define DEBUG(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
in gcc (thanks to M. Oehm).
If you want your \n to be always on the final, you can just add one more printf statement:
#define DEBUG(...) printf(__VA_ARGS__); printf("\n")
...
DEBUG("hello %d", 1);
DEBUG("hello %d", 1);
Outputs:
hello 1
hello 1
As pointed out by the others, this won't work as expected with this scenario:
if (cond)
DEBUG("Blah")
So you will have to define the macro this way:
#define DEBUG(...) do { printf(__VA_ARGS__); printf("\n"); } while(0)
Thanks to M. Oehm and undur_gongor
You could do this with string literal concatenation, if you know that the first argument is always a string literal.
If you have a macro
#define EXAMPLE(A,B) \
printf("%s", A B)
then in code
EXAMPLE("foo ", "bar\n");
would be the same as
printf("%s", "foo bar\n");
(Since you didn't show full code, I assume you can adapt this to your case)

how to substitute function using macro in C

Sometimes, in a C code I borrowed from a linux driver, I want to change some macros into a function that I can use in my environment. But this previous macro can take 3 or 4 arguments.
For example, if I want to substite
SMSC_TRACE(pdata, probe, "Driver Parameters:"); // 3 arguments
into
printf("Driver Parameters:");
and substitute
SMSC_TRACE(pdata, probe, "LAN base: 0x%08lX", (unsigned long)pdata->ioaddr); // 4 arguments
into
printf("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
How do I do that? I tried
#define SMSC_TRACE((a), (b), (c)) printf((c))
#define SMSC_TRACE((a), (b), (c), (d)) printf((c), (d))
but it doesn't seem to work. Only the last one seems to take effect.
EDIT: this seems it maybe.
#define SMSC_TRACE(pdata, nlevel, fmt, args...) printf(fmt "\n", ##args)
You can do it with a variadic macro, which takes a variable number of arguments:
#define SMSC_TRACE(a,b,...) printf(__VA_ARGS__)
If you want this macro to execute multiple statements, then you need a do/while(0).
For example:
#define SMSC_TRACE(a,b,...) \
do \
{ \
printf("%c\n",a); \
printf("%d\n",b); \
printf(__VA_ARGS__); \
} \
while (0)

Checking if an argument is passed in variadic macros in C

For cleaner error handling I use a macro (it uses C99 and GCC extensions); the behavior is like standard assert:
#define A(cond, msg, ...) ({ \
if (!(cond)) { \
if (msg) \
say(msg, ##__VA_ARGS__); \
else \
say("Error at file %s, function %s, line %u: %s", \
__FILE__, __func__, __LINE__, #cond); \
A_RETURN(); \
} \
})
where say is a formatted output. And use it like that:
#undef A_RETURN
#define A_RETURN() ({ fclose(f); free(p); return false; })
A(foo() != FOO_ERROR, 0);
A(bar() != BAR_ERROR, "bar failed");
When I don't have a specific error message, I have to write A(cond, 0). But I want just write A(cond) in this case. How to modify my A macro for this behavior? I.e. I need a way to check if msg argument isn't passed to the macro.
From the help of suggested question I came to the point that you can modify your macro like this.
#define A() .... //your macro
#define A_CALC_ARG_IMPL(_1,N,...) N
#define A_CALC_ARG(...) A_CALC_ARG_IMPL(__VA_ARGS__,0)
#define A_NEW(...) A(cond, A_CALC_ARG(__VA_RGS__))
so your new A_NEW macro call will be expanded to A(cond, 0) if you don't pass msg.
Variadic macro is explained nicely at this blog.

Undefined variable error in C macros (for customized prints)

I'm trying to make a custom printf that prints the file / line no , along with the error message , depending on the current print level set. I've defined a macro for the same. Given below is the code for the preprocessor:
#define DIE (s) \
printf(s); \
exit(0); \
#define my_print(level,s) \
if(level <= gPrintLevel) \
{ \
char *buffer = (char *)malloc(strlen(s)-1); \
if (NULL != buffer) \
{ \
sprintf(buffer,s); \
printf("[%s][%d]:%s\n",__FUNCTION__,__LINE__,buffer); \
if (level == fatal) \
{\
DIE(s);\
}\
} \
} \
I'm calling the above pre-processor like this from inside a function:
myPrint(2,"Unexpected error encountered\n");
But, I'm getting the below compile errors when I try to compile:
41: error: ā€˜sā€™ was not declared in this scope
Please help, what am I doing wrong ? Also, its appreciated if someone can tell me if there's a more elegant way of having customized print statements as above. Thanks in advance.
Personally, I would simply assume or mandate that the user provide a literal format string. In that case, you can concatenate strings:
#define MYPRINT(fmt, ...) \
printf("Function: %s. Line: %d. " fmt "\n", \
__FUNCTION__, __LINE__, ## __VA_ARGS__);
Usage:
MYPRINT("The flargle %d has unexpected grobule %f", f->q, f->r);
This approach also lets you take advantage of the compiler's ability to analyze the format string statically and warn you about mismatching arguments.
(The code uses a GCC extension involving ## to elide the final comma in case the argument list is empty.)
OK thanks for all the help guys, the variadic macros solution works fine. This is the new defn of the macro now:
#define DIE(fmt) \
do { \
printf(fmt); \
exit(0); \
} while(0); \
#define my_print(x,fmt,...) \
if (x < gPrintLevel) \
{ \
printf("[%s][%u]:" fmt "\n",__FUNCTION__,__LINE__,##__VA_ARGS__); \
if (fatal == x) \
{\
DIE(fmt) \
}\
} \

Resources