4

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.

John
  • 861
  • 1
  • 7
  • 19

3 Answers3

3

As of C11, you can use the _Generic keyword. This allows you to check the type of any value or variable. According to this document, _Generic has behaviour that varies between compilers. This answer provides a simple solution, though, using the comma operator.

#define PRINT(fst, ...) \
( \
    _Generic((0, fst), char *: 1, default: 0) ? \
    PRINTNL(fst, __VA_ARGS__) : \
    PRINTL(fst, __VA_ARGS__) \
)

Where PRINTNL prints without the number and PRINTL prints with the number.

Rest of the code:

#define PRINTNL(...) printf(__VA_ARGS__)
#define PRINTL (n, ...) ({ \
    printf("%d: ", n); \
    printf(__VA_ARGS__); \
})
cdo256
  • 973
  • 1
  • 11
  • 16
1

Since you'd know by the time of writing whether the first argument is a number prefix or not, make a macro by another name for prefixing with the number. Here I assume that PRINT(...) expands to printf(__VA_ARGS__):

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

So define a macro NPRINT that calls printf twice, once to output the prefix with number and once with the format:

#define NPRINT(number, fmt, ...) (printf("%d: ", number), printf(fmt, __VA_ARGS__))

Usage

#include <stdio.h>

int main(void) {
    NPRINT(1, "%s\n", "hi");
}

Of course this doesn't work if the call to printf was supposed to be atomic - now if the format string was always a literal string, then you could use string concatenation:

#define NPRINT(number, fmt, ...) (printf("%d " fmt, number, __VA_ARGS__))

If it can be a variable and only one call to PRINT is allowed, the only portable way that I could see is to make a function that builds the format.

With the latest edit that without the number argument, -1: should be prefixed, this would simply become:

#define PRINT(...) NPRINT(-1, __VA_ARGS__)
  • The problem here is that I will have to use NPRINT to print with the number prefix, and I can only use PRINT for this purpose – John Aug 06 '17 at 12:22
  • @John the only possible solution is if you can use C11 and `_Generic` selection. There is absolutely no other way in standard C then. – Antti Haapala -- Слава Україні Aug 06 '17 at 12:26
  • This is certainly the correct approach. Overloading (a form of polymorphism; essentially what the OP is asking for) is a good way to adapt to a type system to get one _conceptual_ function to do _one thing_, but it's wrongheaded if you're asking one conceptual function to do _many things_. `PRINT(` [...arguments...] `)` is one thing. `PRINT(` , , [...arguments...] `)` is another thing. Two things should not both be spelled `PRINT`; that's bad design. – H Walters Aug 06 '17 at 18:39
0

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!")
Ganindu
  • 67
  • 4