4

Let’s say there is a debugging function, simplified here as:

void DumpString(char* var, char* varname) {
    printf("%s : '%s'\n", varname, var);
}

char str[10]="foobar";
DumpString(str, "str");

> str : foobar

Let’s make it easier by removing the unnecessarily extraneous requirement of passing the variable twice, once in quotes:

#define VARASSTR(v) #v

void DumpString(char* var) {
    printf("%s : '%s'\n", VARASSTR(var), var);
}

char str[10]="foobar";
DumpString(str);

> var : foobar

Oops! It uses the local variable name instead of the one passed in. Let’s try a different (and less ideal) tack:

#define DumpStr(v) DumpString(v, #v)

void DumpString(char* var, char* varname) {
    printf("%s : '%s'\n", varname, var);
}

char str[10]="foobar";
DumpStr(str);

> str : foobar

Great it works. But what if the function was a little more complicated:

void DumpString(char* var, char* varname, int optionalvar=0) {
    printf("%s : '%s'\n", varname, var);
    printf("blah: %d", optionalvar);
}

It is not possible to overload a macro, so DumpStr won’t work, and we have already ruled out the version with VARASSTR.

How can this be handled (without resorting to multiple similarly, but differently-named functions/macros)?

Synetech
  • 9,643
  • 9
  • 64
  • 96

1 Answers1

1

This is non-standard, but works as an extension in GNU C:

#define DumpStr(v, ...) DumpString(v, #v, ##__VA_ARGS__)

In GNU C, you can pass no arguments to a variadic macro, and the "token pasting operator" ## when applied between a comma and an empty variadic argument list produces nothing (so the trailing comma is suppressed).

In Visual C++, I believe the token pasting operator ## is unnecessary (and will probably break the macro), as Visual C++ automatically suppresses a trailing comma if it appears before an empty variadic argument list.

Note that the only thing that makes this nonstandard is the desire to sometimes pass an empty argument list. Variadic macros are standardized in both C99 and C++11.


Edit: And here's an example that doesn't use non-standard features. You can see why some people really, really wish this sort of thing was addressed in the standard:

#define DUMPSTR_1(v) DumpString(v, #v)
#define DUMPSTR_2(v, opt) DumpString(v, #v, opt)
#define DUMPSTR_NARG(...) DUMPSTR_ARG_N(__VA_ARGS__, 4, 3, 2, 1, 0)
#define DUMPSTR_ARG_N(_1, _2, _3, _4, n, ...) n
#define DUMPSTR_NC(f, ...) f(__VA_ARGS__)
#define DUMPSTR_NB(nargs, ...) DUMPSTR_NC(DUMPSTR_ ## nargs, __VA_ARGS__)
#define DUMPSTR_NA(nargs, ...) DUMPSTR_NB(nargs, __VA_ARGS__)
#define DumpStr(...) DUMPSTR_NA(DUMPSTR_NARG(__VA_ARGS__), __VA_ARGS__)

There's probably a few cleaner ways to do this. But not that many.


Edit 2: And here's yet another example that doesn't use non-standard features, courtesy of R..

#define STRINGIFY_IMPL(s) #s
#define STRINGIFY(s) STRINGIFY_IMPL(s)
#define ARG1_IMPL(a, ...) a
#define ARG1(...) ARG1_IMPL(__VA_ARGS__, 0)
#define DumpStr(...) DumpString(STRINGIFY(ARG1(__VA_ARGS__)), __VA_ARGS__)

Note that this requires the argument order of the DumpString to be changed so that the stringified function name is the first argument.

Community
  • 1
  • 1
John Calsbeek
  • 35,947
  • 7
  • 94
  • 101