How to write a macro with optional and variadic arguments

Viewed 2379

I have a macro called PRINT(...) that I use in my code, which gets a variable number of arguments and acts like printf (gets a format and arguments). It's defined like this:

#define PRINT(...) PRINT(__VA_ARGS__)                     

Now I want to modify it so it will have an optional argument, say that its name is number and it will add a numeric prefix to the printing. For example:

PRINT("%s", "hi") -> will print hi
PRINT(1, "%s", "hi") -> will print 1: hi

How can I change the PRINT macro to support this?
Important to say, that I don't want to change any existing call to this macro from my code (in the example, if I have a call to PRINT("%s", "hi") - it needs to remain the same after the change).
Also, I can't create new macro for this purpose- must use the existing PRINT macro for this purpose (but off course I can change it's arguemnts definition).
Any idea how can I do this?

Edit: I saw this post about variadic macro- but It's different from what I'm asking here since the argument number needs to be a recognized variable, which will be treated in the implementation of PRINT as -1 if the call to PRINT doesn't contain the number argument (-1 will be an indicator for printing no number) and otherwise it will print the number prefix.

3 Answers

Please have a look at the ##__VA_ARGS__ macro. Also check the code below written for a log function.

In the header file

/**
 * ##__VA_ARGS__ allows us to make varadic arguments optional
 * https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html
 * also check __VA_OPT__ (C++20)
 */ 

#define APP_LOG(message, ...) \
    do { \
            APP_LOGX( __FILE__, __LINE__, ("\e[1;34m[INFO]: \e[0m" message), ##__VA_ARGS__); /** BLUE */ \
    } \
    while(0)

#define APP_ERROR(message, ...) \
    do { \
            APP_LOGX( __FILE__, __LINE__, ("\e[1;31m[FATAL]: \e[0m" message), ##__VA_ARGS__); /** RED */ \
    } \
    while(0)

In the implementation

/** #include <libgen.h> */
void APP_LOGX(const char * file, int num, const char* message, ...)
{
    va_list ap;
    int length;
    char * tfilename = NULL;
    tfilename = strdup(file);
    
    length = strlen(message);
    if(length>0){
        va_start(ap, message);
        vprintf(message, ap);
        printf(" | File: %s Line %d ", basename(tfilename), num);
        va_end(ap);

        /* add newline if nessasary */
        if(message[length -1] != '\n'){
            printf("\n");
        }
    }

    free(tfilename);
}

Application

 APP_LOG("app display init")

or

APP_LOG("Value of x acceleration %.2f", g);
APP_ERROR("Something bad happened!")
Related