What am I doing wrong with this variadic macro in C? - c

I'm working on a university project and I made a macro wrapper of printf which can color the text, print the file, function and line of the instruction with a string error based on errno.
#define PRINT_WITH_COLOR(color, title, message, errno_code) printf(color "[" title "] " message " %s::%s:%d [%s]\n" RESET_COLOR_CONSOLE, __VA_ARGS__, __FILE__, __func__, __LINE__, strerror(errno_code));
This initially was divided in multiple printf but due to multithreading, the problem I'm facing is that when I have no argument to pass the comma remains there and the program doesn't compile.
For that reason I used the ## to remove commas around my variadic parameter:
#define PRINT_WITH_COLOR(color, title, message, errno_code) printf(color "[" title "] " message " %s::%s:%d [%s]\n" RESET_COLOR_CONSOLE, ## __VA_ARGS__, __FILE__, __func__, __LINE__, strerror(errno_code));
I thought it was working because I didn't receive any error on my VS Code but the gcc compiler gives me this error pointing on the right comma next to the __VA_ARGS__ , <----:
expected expression before ‘,’ token
Edit
Here is an example usage:
I use it indirectly by calling this macro
#define PRINT_WARNING(err_code, message, ...) PRINT_WITH_COLOR(YELLOW_COLOR, "Warning", message, __VA_ARGS__)
And in my code I use it like that
PRINT_WARNING(EINVAL, "File %s cannot be written!", pathname);
The output is a yellow message in console [Warning] File foo cannot be written! filepath::func:line [errno msg]

It is actually the macro expansion of PRINT_WARNING that eventually results in the problem, and you should use ## there too. I think you also wanted to pass the err_code as well:
#define PRINT_WARNING(err_code, message, ...) \
PRINT_WITH_COLOR(YELLOW_COLOR, "Warning", message, err_code, ##__VA_ARGS__)
And the PRINT_WITH_COLOR macro should also end in , ...)
#define PRINT_WITH_COLOR(color, title, message, errno_code, ...) \
printf(color "[" title "] " message " %s::%s:%d [%s]\n" RESET_COLOR_CONSOLE, \
##__VA_ARGS__, __FILE__, __func__, __LINE__, strerror(errno_code));

Related

How to make a __VA_ARGS__ macro which can be called with no arguments? [duplicate]

This question already has answers here:
#define macro for debug printing in C?
(14 answers)
Closed 2 years ago.
I have a macro:
#define debug(fmt, ...) printf("%lu %s:%s:%i " fmt, ms(), __FILE__, __func__, __LINE__, __VA_ARGS__)
which does just what I want.
I can call it with:
debug("i: %i\n", i);
to print the value of i.
My problem is that I can't call it with:
debug("got here");
as that expands to:
printf("%lu %s:%s:%i %s " "got here", ms(), __FILE__, __func__, __LINE__,)
which is a trailing-comma bug.
How can I change my __VA_ARGS__ macro so that it can handle the "no variables"/"only format string" case?
You can do it in two steps:
#define debug(...) DEBUG(__VA_ARGS__, "")
#define DEBUG(fmt, ...) printf("%lu %s:%s:%i " fmt "%s", ms(), __FILE__, __func__, __LINE__, __VA_ARGS__)
debug("%d\n", 42);
debug("Hello\n");
In this way, even if you don't pass a second param it is replaced by an "" and results in a NOP.
Instead of trying to concatenate string literals in the macro itself, you can split the printf into two parts: One for the statistics, the other for your debug message. Yoke them together with the old do { ... } while (0) macro trick.
#define debug(...) do { \
printf("%lu %s:%s:%i ", ms(), __FILE__, __func__, __LINE__); \
printf(__VA_ARGS__); \
} while (0)
Then you don't need the fmt as separate argument and can just pass __VA_ARGS__ to the second printf.

Variadic macro expected ')' before numeric constant

this is the actual macro:
#ifdef DEBUG
#define debug(funcname, format, ...) \
printf(BOLD UNDERLINED REVERSE \
"DEBUG IN " __FILE__ \
" LINE " __LINE__ ":" \
RESET UNDERLINED REVERSE \
"In " funcname \
RESET REVERSE format RESET, ##__VA_ARGS__)
#else
#define debug(funcname, format, ...)
#endif
Where all the constant used are well defined string constants.
I call it with something like:
char message[] = "Hello StackOverflow !\n";
debug("main()", "Message: %s\n", message);
But I get the message
error: expected ‘)’ before numeric constant
debug("main()", "Message: ", message); poiting at the closing parenthese.
It is weird because I first tested the macro, and now that the project has advanced with the team it doesn't work...
That's because
" LINE " __LINE__ ":"
expands to the syntactically invalid
" LINE " 42 ":"
since __LINE__ is an integer, not a string literal that can be concatenated.

The strange syntax of a C debug macro

Can anyone explain to me the C-syntax below (from this tutorial)? I understand this is a macro for C, but the "DEBUG %s:%d: " M "\n"
part seems strange to me: why is there the macro parameter 'M' in the middle of the format part?
#define debug(M, ...) fprintf(stderr,
"DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
C has an interesting quirk that it concatenates string literals. If you type
"DEBUG %s:%d: " "HELLO %s!" "\n"
Then the compiler sees that as one string: "DEBUG %s:%d: HELLO %s!\n". So users can use this macro as if it were simply had printf parameters:
debug("HELLO %s", username); //on line 94 of myfile.cpp
and the macro will automatically add the file name and line number. This format is useful because it helps you know which debug statement is logging information.
DEBUG myfile.cpp:94: HELLO zell
debug("This should never happen!");
evaluates to
fprintf(stderr, "DEBUG %s:%d: " "This should never happen!" "\n", __FILE__, __LINE__, ##__VA_ARGS__)
Which concatenates to...
fprintf(stderr, "DEBUG %s:%d: This should never happen!\n", __FILE__, __LINE__, ##__VA_ARGS__)
So it prints something like...
DEBUG foo.c:51: This should never happen!
Strings are joined together automatically in C. "Hello " "World!" is the same as "Hello World!".
DEBUG("An error occurred opening file %s", filename)
Expands to:
fprintf(stderr, "DEBUG %s:%d: An error occurred opening file %s\n", __FILE__, __LINE__, filename)
Which I think you'll agree is pretty handy and the right result.

How to use gettext with __VA_ARGS__ in c

I'm trying to translate my console log with gettext, but I get the follow error:
program.c: In function ‘program_take_screenshot’:
program.c:55:14: error: expected ‘)’ before ‘dcgettext’
#define _(x) gettext(x)
^
program_logger.h:117:49: note: in definition of macro ‘PROGRAM_ERR’
fprintf(LOG_FILE, "Program [ERROR] :: " __VA_ARGS__); \
^
program.c:173:17: note: in expansion of macro ‘_’
PROGRAM_ERR(_("Cannot take screenshot. GPU rendering is used and read_viewport is not supported.\n"));
^
what I do wrong?
definition in program_logger.h:
#define PROGRAM_LOG(...) do { \
if (PROGRAM_LOG_VERBOSE) \
{ \
fprintf(LOG_FILE, "Program: " __VA_ARGS__); \
fflush(LOG_FILE); \
} \
} while (0)
definition of PROGRAM_ERR:
#define PROGRAM_ERR(...) do { \
fprintf(LOG_FILE, "PROGRAM [ERROR] :: " __VA_ARGS__); \
fflush(LOG_FILE); \
} while (0)
Although one of the other answers explains what's going on, it doesn't give you an appropriate means of solving the problem.
What you had:
#define PROGRAM_ERR(...) do { \
fprintf(LOG_FILE, "PROGRAM [ERROR] :: " __VA_ARGS__); \
fflush(LOG_FILE); \
} while (0)
would allow, for example, using it like PROGRAM_ERR("some error: %s", "error message"). Yet as you've found, PROGRAM_ERR(_("some error: %s"), "error message") fails.
The cause is, as explained already, indeed that this expands to
do { fprintf(LOG_FILE, "PROGRAM [ERROR] :: " _("some error: %s"), "error message"); fflush(LOG_FILE); } while(0)
and string concatenation only works for string literals.
In my opinion, the simplest way to make this work, is
#define PROGRAM_ERR(...) do { \
fputs("PROGRAM [ERROR] :: ", LOG_FILE); \
fprintf(LOG_FILE, __VA_ARGS__); \
fflush(LOG_FILE); \
} while (0)
By separating the two strings, you don't need any compile-time string concatenation, which is simply not possible if the strings are not known at compile-time.
Try changing the macro to put ## before __VA_ARGS__. (##__VA_ARGS__) That instructs the preprocessor to place a comma there, but only if there is an argument.
See the gcc documentation here for more details.

"##" in printk, what does ## mean

#define ext4_debug(f, a...) \
do { \
printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", \
__FILE__, __LINE__, __func__); \
printk(KERN_DEBUG f, ## a); \
} while (0)
what I dont understand is this
printk(KERN_DEBUG f, ## a);
Could anybody help me to understand what is ## in this line?
thank you
Its a token for variadic macros(macros with multiple, variable arguments). Its gcc specific directive that allows 0 or more arguments as an input to, after f in ext4_debug(). Which means, f argument is mandatory, a may or maynot exist.
This is same as printf(const char *fmt,...) where, fmt is mandatory, other arguments are optional and dependent on the fmt. See the last statement in this doc: http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
It is there to make the variadic macro (macro which can take multiple arguments) work if you pass in 0 arguments.
From the Variadic Macros section in the GCC manual:
Second, the ## token paste operator has a special meaning when placed between a comma and a variable argument. If you write
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)
and the variable argument is left out when the eprintf macro is used, then the comma before the ## will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ## is anything other than a comma.
eprintf ("success!\n")
==> fprintf(stderr, "success!\n");
If you did not use this, then that would expand to frpintf(stderr, "success!\n",), which is a syntax error.

Resources