3

Disclaimer: I have already looked at the answers to questions similar to Stringification of a macro value.

Consider the following test program:

#include <stdio.h>
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)

#define MACRO 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));
}

The output of this program is:

HelloWorld
MACRO
MACRO
MACRO

The desired output is

HelloWorld
MACRO

MACRO

Is there a way to redefine my macros (that is, the meta-macros which are not MACRO) to get the desired output?

In particular, I want it to be the case that the third printf should be equivalent to:

printf("%s\n", QUOTE());

That is, I want the expansion of the undefined macro into "nothingness" and then have that "nothingness" passed to the quote function.

Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • Why would you expect an undefined macro like `MACRO` to expand to an empty string? If its not defined, it isn't expanded or changed at all... – Chris Dodd Oct 26 '13 at 00:20
  • I guess that's true, I was thinking of how if you do `#define MACRO` without a definition, it simply goes away. – merlin2011 Oct 26 '13 at 00:24

5 Answers5

3

This is not possible in standard C.

There are four things that can happen to a token during macro replacement. It can remain unchanged, it can be substituted with a replacement value, it can be concatenated with another token, and it can be stringized.

If MACRO is unchanged, there is no way to distinguish these two situations:

#undef MACRO
printf("%s\n", EXPAND_AND_QUOTE(MACRO));

#define FOO MACRO
printf("%s\n", EXPAND_AND_QUOTE(FOO));

Once FOO is replaced in the latter, we have two situations: In one, we have MACRO because it is unchanged. In the other, we have MACRO because it was substituted for FOO. After that point, the behavior must be identical. But the problem requirements call for different behavior, "" for the former and "MACRO" for the latter. Since identical behavior cannot produce different results, this will not work. (But more about this below.)

The second possibility, substitution with another value, does not happen, since MACRO is not defined.

The third possibility merely produces some new token, such as FOOMACRO. However, although we can choose the first part, we cannot choose the latter part, so we cannot know what token will result, and there is nothing we might do with it that would differ depending on whether MACRO is defined or not.

The fourth possibility has the same problem as the third, we produce "MACRO" and can make no use of it.

The only vague hope I had of doing something with this is producing two tokens, one the result of letting the token be replaced and one not. E.g., we can produce a situation in which, given the above non-definition of MACRO and the definition of FOO, EXPAND_AND_QUOTE(MACRO) produces, somewhere in a chain of expansions of helper macros, two tokens SomethingMACRO and SomethingMACRO, while EXPAND_AND_QUOTE(FOO) produces SomethingFOO and SomethingMACRO. That is possible, but then what do we do with the two tokens? We could stringize both and then compare them with strcmp at run time. However, I do not think anything can be done at compile time. Also, it would fail to distinguish the situation in which FOO is defined as #define FOO FOO; that would, if we were successful in comparing the tokens described above, produce "", but the requirements call for it to produce "FOO".

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

Try this:

#undef MACRO
#define MACRO

Alternatively:

#undef MACRO
#ifndef MACRO
#  undef EXPAND_AND_QUOTE
#  define EXPAND_AND_QUOTE(str) ""
#endif
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    I believe the goal is to define `EXPAND_AND_QUOTE` to produce the indicated results, not to change `MACRO`. – Eric Postpischil Oct 25 '13 at 23:30
  • 1
    @EricPostpischil: I guess that's debatable -- the question was "how to redefine my macros"... but please do post an alternative answer. – Kerrek SB Oct 25 '13 at 23:36
  • Eric is correct. I thought that what he said was implicit in the question, but I just made it more explicit. – merlin2011 Oct 26 '13 at 00:13
1

Instead of #undef MACRO try:

#undef MACRO
#define MACRO \0

The \0 ensures that MACRO can be passed (because some compilers cough VC++ cough can't parse MACRO without it) but is still a blank string. \0 is an escape character for an ASCII end of string, in essence setting MACRO to a blank string.

Sam Cristall
  • 4,328
  • 17
  • 29
1

The question in the title literally makes no sense -- how can an undefined macro expand? Its undefined, so there's nothing to expand. The solution to get the output you say you want is to add

#define MACRO

right after the #undef -- that is, define MACRO as a macro that expands to nothing.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • +1 for pointing out that my question is stupid....I've just been trying to find some way of checking for the existence of a macro without putting #ifdef wrapper on every possible macro (or predefining it), and I think I'm overthinking it too much. – merlin2011 Oct 26 '13 at 00:25
-1

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.

tel
  • 13,005
  • 2
  • 44
  • 62
  • Thanks @tel for sharing. While there is some hack value here, this does not technically answers the question, and it seems only applicable if the pre-existing macro fits a 0, something pattern, which does typically not happen. I really wish it could be changed to actually answer the question, but cannot find a way. Anyone, if this can be improved, please enlighten me. Thanks in all cases. – Stéphane Gourichon Oct 06 '20 at 08:19