Question

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.

Was it helpful?

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.

OTHER TIPS

Why don't you use 2 different macros for each verbosity level; one which prints a message and variable, and one which just prints a message?

You should probably write yourself a small support function so that you can do the job cleanly:

extern void vb_print(const char *format, ...);

#define V1(...)  do { if (optv >= 1) vb_print(__VA_ARGS__); } while (0)

I assume that both optv and prog are global variables. These would go into a header (you wouldn't write them out in the programs themselves, would you?).

The function can be:

#include <stdio.h>
#include <stdarg.h>

extern const char *prog;

void vb_print(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    printf("%s:", prog);
    vprintf(format, args);
    va_end(args);
}

There's no rocket science in there. You can tweak the system to your heart's content, allowing a choice of where the information is written, flushing the output, ensuring there's a newline at the end, etc.

Try this:

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

I believe that fixes the problem you described, and the else at the end fixed another problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top