0

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.

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Huckle
  • 1,810
  • 3
  • 26
  • 40

4 Answers4

5

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.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I was hoping to not need to implement a function, but based on the other answers it seems I have little choice. – Huckle Feb 25 '12 at 02:57
  • On second thought, `printf("%s: ",prog);` gave me a good idea: `#define V1(...) if(optv>=1){printf("%s: ",prog);printf(__VA_ARGS__);}` – Huckle Feb 25 '12 at 03:14
  • wrapping your function-like macro in a do while(0) loop could help – tarabyte May 29 '15 at 17:48
1

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?

Alex D
  • 29,755
  • 7
  • 80
  • 126
  • Because I already have 5 verbosity levels, and I also have == versions of the same. So that means duplicating 10 macros. – Huckle Feb 25 '12 at 02:59
1

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711