Search code examples
cmacrosverbosity

Good Verbosity Macro (C99)


I'm looking to write what I would imagine is a fairly common macro. I want to emulate the repeated "-v" options on many POSIX programs by defining a bunch of macros of the following form:

#define V1(str, ...) if(optv >= 1){printf("%s: "str,prog,__VA_ARGS__);}

int main(int argc, char* argv[])
{
  // ... stuff ...
  int i = 1;
  V1("This contains a variable: %d\n",i);
}

// Output:
// ./program: This contains a variable: 1

where optv counts the number of "-v" options found on the command line and prog contains the program name (neither shown). This works well, but the problem is that I have to use a variable. V1("Output") will generate a compiler error. I could always use V1("Output%s","") but there should be a cleaner solution.


Solution

  • The GNU C preprocessor has a special feature that lets you delete the trailing comma when there are no arguments filling the variadic portion by prepending the token-pasting operator ## to __VA_ARGS__:

    #define V1(str, ...) if(optv < 1); else printf("%s: "str,prog, ## __VA_ARGS__)
    

    Alternatively, if you wish to remain fully C99 compliant, you could incorporate the the format string parameter into the ellipsis, but in this instance you'll also need to refactor your code since you want to include the extra prog parameter between the format string and the varargs. Something like this might work:

    #define V1(...) if(optv < 1); else myprintf(prog, __VA_ARGS__)
    int myprintf(const char *prog, const char *fmt, ...)
    {
        // Print out the program name, then forward the rest onto printf
        printf("%s: ", prog);
    
        va_list ap;
        va_start(ap, fmt);
        int ret = vprintf(fmt, ap);
        va_end(ap);
    
        return ret;
    }
    

    Then, V1("Output") expands to myprintf(prog, "Output") without using any non-C99 compiler extensions.

    EDIT

    Also note that I inverted the if condition in the macro, due to some weird issues that can arise if you invoke the macro inside an if statement without braces—see this FAQ for a detailed explanation.