This can be done, with two caveats:
- Your compiler must support variadic macros (they were part of the C99 standard)
You have to add a 0,
when defining your macro token, ie
#define MACRO 0, HelloWorld
Given the above, you can use this nifty RESOLVE
macro I came up with. Here's a one-line version (it can be made more bulletproof/portable):
#define RESOLVE(token, default_token, ...) default_token
and here it is applied to your sample code:
#include <stdio.h>
#define BLANK
#define RESOLVE(token, default_token, ...) default_token
#define QUOTE(str) #str
#define QUOTE0(str) QUOTE(str) // need the intermediate QUOTE0 function here or else you get output like `RESOLVE(0, HelloWorld, , )` instead of `HelloWorld`
#define EXPAND_AND_QUOTE(str, ...) QUOTE0(RESOLVE(str, BLANK, __VA_ARGS__)) // the BLANK token is optional here, can also be a literal blank
#define MACRO 0, HelloWorld
int main() {
printf("%s\n", EXPAND_AND_QUOTE(MACRO));
printf("%s\n", QUOTE(MACRO));
#undef MACRO
printf("%s\n", EXPAND_AND_QUOTE(MACRO));
printf("%s\n", QUOTE(MACRO));
}
When I compile and test it the output is indeed
HelloWorld
MACRO
MACRO
how RESOLVE
works
The basic idea is that while the preprocessor cannot distinguish between defined and undefined tokens (since it doesn't distinguish between undefined tokens and regular text), it can distinguish between single tokens and tuples, at least within the parameter lists of function-like macros. __VA_ARGS__
then allows a macro to produce different behavior on the basis of this distinction.
Say you have two defined tokens
#define BAR bar
#define BAZ baz0, baz1
and an undefined token FOO
. When you pass these to RESOLVE
, they effectively expand to
RESOLVE(FOO, BLANK) -> RESOLVE(token=, default_token=BLANK, __VA_ARGS__=) -> BLANK
RESOLVE(BAR, BLANK) -> RESOLVE(token=bar, default_token=BLANK, __VA_ARGS__=) -> BLANK
RESOLVE(BAZ, BLANK) -> RESOLVE(token=baz0, default_token=baz1, __VA_ARGS__=BLANK) -> baz1
In the case of BAZ
, BLANK
gets shoved off of the end of the named parameter list (and into the ellipsis) by the second value stored in BAZ
, which is then returned.