Variadic macro expected ')' before numeric constant - c

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.

Related

What am I doing wrong with this variadic macro in 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));

How to stringify char buffer for C macro

I have a LOG(fmt, ...) macro that does not work when using a char buf[] as fmt.
The code below is a complete (not actually) working example of the code. In some_function(), I am trying to use LOG() in two different ways but only the first approach works.
To solve the problem, I have tried using #define LOG_STR(x) #x in the following way:
To stringify what is received in the #define LOG by applying LOG_STR() to format like this: LOG_STR(format); and
To apply LOG_STR() directly to the printing like this: LOG(LOG_STR(fmt), 6).
Neither approach works and in fact I get segfault out of it.
#include <stdio.h>
#define LOG(format, ...) do { \
fprintf(stderr, "[LOG] " format " [%s]\n", \
##__VA_ARGS__, __func__); \
} while (0)
static void some_function()
{
// This works
LOG("This is a number: %d", 5);
// This does not work
const char fmt[] = "This is a number: %d";
LOG(fmt, 6);
}
int main(void)
{
some_function();
return 0;
}
When I compile the code above I get the following error:
$ gcc -o log-macro-str log-macro-str.c
log-macro-str.c: In function ‘some_function’:
log-macro-str.c:15:6: error: expected ‘)’ before ‘fmt’
LOG(fmt, 6);
^
log-macro-str.c:4:29: note: in definition of macro ‘LOG’
fprintf(stderr, "[LOG] " format " [%s]\n", \
^~~~~~
I'd like to use LOG() in both ways as done in some_function() or without modifiers and just printing a string. I suspect I might have to stringify the format part but I can't seem to do it correctly.
What am I doing wrong, and how can I solve this issue?
The stringify operator, # in a macro, converts preprocessor tokens to text in a string literal. It will not change the contents of a char buffer to a compile-time string literal.
To make your macro work, use multiple fprintf statements:
#define LOG(format, ...) do { \
fprintf(stderr, "[LOG] "); \
fprintf(stderr, format, __VA_ARGS__); \
fprintf(stderr, " [%s]\n", __func__); \
} while (0)

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.

fprintf macro with single param expecting ')'

I got next debug macro, which works fine:
#ifndef NDEBUG
#define errorLog(fmt, ...) fprintf(stderr, "[ERROR %s: %d] " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
Except if i try next line (which produce expected a ')' error):
const char* someMessage = "Message";
errorLog(someMessage);
But this one produce no errors:
errorLog("Message");
Right now i solve this like:
const char* someMessage = "Message";
errorLog("%s", someMessage);
How to change macro to work with single const char* param as well?
const char* someMessage = "Message";
errorLog(someMessage); //fprintf(stderr, someMessage)
As written, your errorLog requires a string literal for the first (fmt) argument, as it tries to use string concatenation, which only works with string literals. If you want a macro that can take any char *, not just a string literal, you have to use it directly, rather than pasting. You could use something like:
#define errorLog(...) \
(fprintf(stderr, "[ERROR %s: %d] ", __FUNCTION__, __LINE__), \
fprintf(stderr, __VA_ARGS__), \
fprintf(stderr, "\n"))
This will work for any char *, but will not have the same return value, if you care about that. This also has the advantage that it doesn't depend on the gcc , ## __VA_ARGS__ extension which may not work on other compilers.
If you want this to work properly in a multithreaded program, you might need:
#define errorLog(...) \
(flockfile(stderr), \
fprintf(stderr, "[ERROR %s: %d] ", __FUNCTION__, __LINE__), \
fprintf(stderr, __VA_ARGS__), \
fprintf(stderr, "\n"), \
funlockfile(stderr))
instead

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.

Resources