cc89variadic-macros

Variadic macros alternative in ANSI C


I know that variadic macros have been added in C99 (and via GNU extensions). I've been wondering if there is a nice alternative in ANSI C.

I've come up with something like this, but it's still kind of awkward:

void log_info(const char *file, int line, const char *fmt, ...)
{
#ifdef DEBUG
    va_list ap;
    char newfmt[1024] = { 0 };
    va_start(ap, fmt);
    sprintf(newfmt, "[INFO] (%s:%d): %s\n", file, line, fmt);
    vfprintf(stderr, newfmt, ap);
    va_end(ap);
#endif
}

So that this can be called like this:

log_info(__FILE__, __LINE__, "info message: %s %d", "helloworld", 12);

There is nothing wrong with this approach, however I'm wondering if there is a nicer way of doing it? Eg. with no need to specify file/line everytime.

I'd appreciate any feedback. :)

Edit: By ANSI C here I mean C89.

Edit: The answer below is fine but I believe given it requires running the printing command twise it may impose some thread-safety issues as it is. Another alternative might be to use define to minimize the typing (also quite ugly):

#define __FL__ __FILE__, __LINE__

and then run the command like:

log_info(__FL__, "info message: %s %d", "helloworld", 12);

Solution

  • It's a little ugly, but a sequence of comma-separated expressions in parentheses can be treated as a single argument.

    An example:

    #include <stdio.h>
        
    #define LOG(args) (printf("LOG: %s:%d ", __FILE__, __LINE__), printf args)
        
    int main(void) {
        int n = 42; 
        LOG(("Hello, world\n"));
        LOG(("n = %d\n", n));
        return 0;
    }   
    

    The output:

    LOG: c.c:6 Hello, world
    LOG: c.c:8 n = 42
    

    Note that this requires an extra set of parentheses in the call.

    The preprocessed code will look like:

    #include <stdio.h>
    
    int main(void)
    {   
        int n = 42; 
        (printf("LOG: %s:%d ", "test_variadic.c", 7), printf ("Hello, world\n"));
        (printf("LOG: %s:%d ", "test_variadic.c", 9), printf ("n = %d\n", n));
        return 0;
    }