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__);}
Related
I am trying to make a function similar to printf(), I want it to take multiple arguments so that I can use them when printing, roughly like the following:
void PRINT_RED(string, ...) {
fprintf(stderr, "\033[91m");
fprintf(stderr, "%s", string, __va_arg_pack());
fprintf(stderr, "\033[0m");
}
Take in consideration that I want it to have the following example API PRINT_RED("%s %s %d", string1, string2, int1);.
But as a macro, so I tried:
#define PRINT_RED(string, ...) \
fprintf(stderr, "\033[91m"); \
fprintf(stderr, "%s", string, __va_arg_pack()); \
fprintf(stderr, "\033[0m");
And apparently __va_arg_pack() can only be defined in inline functions... Can anybody guide me on how to handle multiple arguments in a macro function?
What you're looking for is the macro __VA_ARGS__, which translates to the variable arguments passed to a macro.
#define PRINT_RED(string, ...) \
fprintf(stderr, "\033[91m"); \
fprintf(stderr, "%s", string, __VA_ARGS__); \
fprintf(stderr, "\033[0m");
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.
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)
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.
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