The strange syntax of a C debug macro - c

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.

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 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 arguments don't work in AIX 5.3 on CC compiler

I currently have a bunch of debug macros (hijacked from Zed's book Learn C The Hard Way) and I'm trying to compile them on AIX. The macros:
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, s ...)
#else
#define debug(M, s ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ## s)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, s ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ## s)
#define log_warn(M, s ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ## s)
#define log_info(M, s ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ## s)
#define check(A, M, s ...) if(!(A)) { log_err(M, ## s); errno=0; goto error; }
#define sentinel(M, s ...) { log_err(M, ## s); errno=0; goto error; }
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, s ...) if(!(A)) { debug(M, ## s); errno=0; goto error; }
#endif
When I compile my project that imports these macros, the AIX CC compiler prints compiler errors with this message, and then exits normally:
"src/dbg.h", line 13.19: 1506-211 (S) Parameter list must be empty, or consist of one or more identifiers separated by commas.
It prints one of these to every line in the project that use one of the macro functions.
I've tried setting #pragma langlvl (stdc99) and #pragma langlvl (extc99) as advised in this article with no success.
I've also written a small example to see if I can compile it, as follows:
/* file "test.c" */
#include <stdio.h>
#define PRINTERROR(M, s...) fprintf(stderr, "ERROR MSG: " M "\n", ## s)
int main(void) {
PRINTERROR("no args");
PRINTERROR("with args: %s", "foo");
return 0;
}
The compiler emits the following message:
"test.c", line 4.24: 1506-211 (S) Parameter list must be empty, or consist of one or more identifiers separated by commas.
I'm using AIX 5.3 and CC for AIX version 6.0.0.0.
Your code -- either the original version or the edited one, which is using a non-standard extension, see the answer by ouah -- compiles fine for me on AIX 6.1 using XL C/C++ v11.1.
You state that your compiler is CC for AIX version 6.0.0.0. If that is to mean "IBM VisualAge C++ Professional for AIX, V6.0", that version was announced in 2002, i.e. is not really up to date...
Variadic macros in turn were included in the standard only in 1999, so it's quite possible your version of the compiler does not support them yet.
The form:
#define debug(M, s ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ## s)
with s ... is not C but a GNU extension to C. See gcc documentation that states:
If your macro is complicated, you may want a more descriptive name for the variable argument than VA_ARGS. CPP permits this, as an extension. You may write an argument name immediately before the ‘...’; that name is used for the variable argument. The eprintf macro above could be written
#define eprintf(args...) fprintf (stderr, args)
using this extension.
The correct C form for your debug macro would be:
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__,
__LINE__, __VA_ARGS__)

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

concatenating strings in a printf statement

gcc 4.7.2
c89
Hello,
#define LOG_ERR(fmt, ...) \
fprintf(stderr, "[ERROR] %s:%d: error [%s] " fmt "\n", __func__, __LINE__, strerror(errno), ##__VA_ARGS__)
And I am using it like this:
LOG_ERR("Failed to connect to message queue [ %d ]", msg_id);
The fmt has been concatenated in the fprintf statement. How is this possible?
I have tried to do the same with the following below just to test the concept, but failed with a compile error:
/* Using char array */
const char name[] = "Joe";
printf("Hello how " name " how are you today?\n");
Using constant string literal
const char *name = "Joe";
printf("Hello how " name " how are you today?\n");
Both game me the following error:
expected ')' before name
Many thanks for any suggestions,
You need a format specifier, check out the documentation for printf
const char *name = "Joe"; // or your char array, either's fine
printf("Hello how %s how are you today?\n", name);
Your attempt:
printf("Hello how " name " how are you today?\n");
Looks a little more C++ish
cout << "Hello how " << name << "are you today?\n";
When it fails, it's because you're using variables. String literals can be concatenated by the compiler i.e. if you write "abc" "123" then the compiler will treat this as "abc123". And when you do this in the macro, the preprocessor means that this is exactly what is sent to the compiler
LOG_ERR("Failed to connect to message queue [ %d ]", msg_id);
becomes
fprintf(stderr, "[ERROR] %s:%d: error [%s] " "Failed to connect to message queue [ %d ]" "\n", myfunction, 123, strerror(errno), msg_id);
Might be worth checking out the stringizer and concatenation macros too (for the preprocessor - # and ## and I never remember which is which...)
Andrew
Difference is that the macro is textually replacing the word fmt with your string. Putting two or more literal strings together gets you the concatenation of those strings.
"Hello " "World" "!!"
/* is the same as */
"Hello World!!"
Remember, only literal strings will do this. This does not work for variables.
Think of macros like a find/replace in your code. To illustrate, consider your macro definition -
#define LOG_ERR(fmt, ...) \
fprintf(stderr, "[ERROR] %s:%d: error [%s] " fmt "\n",
__func__, __LINE__, strerror(errno), ##__VA_ARGS__)
When you use it like this -
LOG_ERR("Failed to connect to message queue [ %d ]", msg_id);
It does a textual replacement and becomes -
fprintf(stderr, "[ERROR] %s:%d: error [%s] " "Failed to connect to message queue [ %d ]" "\n",
__func__, __LINE__, strerror(errno), msg_id)
The side-by-side strings and concatenated and voila -
fprintf(stderr, "[ERROR] %s:%d: error [%s] Failed to connect to message queue [ %d ]\n",
__func__, __LINE__, strerror(errno), msg_id)
If you want to define a macro for fprintf() you may do like this which is simplest IMHO.
#define LOG_ERR(...) fprintf(stderr, __VA_ARGS__)
and use it like
int main()
{
int myVar =100;
LOG_ERR ("The value of myVar is %d", myVar);
return 0;
}
#include <stdio.h>
#include <sys/time.h>
#include <string>
#define MyPrint(...) {\
struct timeval tv;\
gettimeofday(&tv,0);\
printf("%d.%d:", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);\
printf(__VA_ARGS__);}

Resources