Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use variadic arguments with printf in a macro?

I have found no way to merge the first printf into the second:

unsigned get_time_now(void) {return 1;}
#define DEBUG_PRINT 1
#define debug_tcprintf(fmt, ...) do { \
            if (DEBUG_PRINT) { \
                unsigned p_time_now = get_time_now(); \
                printf ("%u ms ", p_time_now); \
                printf(fmt, __VA_ARGS__); \
            } \
        } while (0)

I need to get this done to get an atomic debug_tcprintf. The macro above has been taken from this Stack Overflow question.

I am doing code in XC that runs on an XMOS multi-logical-core processor. It compiles XC, C and C++, but the code example is from a C code part. It is similar for XC except that it has a timer defined in the language.

If it's not possible to merge the two in one printf, an option may perhaps be to create a string and use sprintf instead? I'd rather not, since such an array might easily overflow.

like image 913
Øyvind Teig Avatar asked Aug 29 '17 17:08

Øyvind Teig


1 Answers

You need to use string concatenation and token pasting. Notice in the snippet below there is no comma after the first string literal -- this is intentional.

#define debug_tcprintf(fmt, ...) do { \
    if (DEBUG_PRINT_HTTPD) { \
        unsigned p_time_now = get_time_now (); \
        printf ("%u ms " fmt, p_time_now, ##__VA_ARGS__); \
    } \
} while (0)

The string concatenation allows you to prepend the "%u ms " portion onto the supplied format string. Token pasting (the ## operator) accounts for the possibility of your macro being invoked with or without additional variadic arguments (beyond just the format string).

This only works if you call the macro with a string literal as the format string.


Addendum: The way token-pasting is used in this example is actually a gcc extension to the standard C pre-processor. If you are not using the gcc compiler, you may need to omit the ## operator. The downside to this is that you then cannot call your macro with only one argument; for example, debug_tcprintf ("hello world") will not work. A simple workaround is to use debug_tcprintf ("%s", "hello world") in such a scenario.

like image 196
Woodrow Barlow Avatar answered Sep 21 '22 22:09

Woodrow Barlow