views:

442

answers:

1

Consider the following macro definitions and invocation:

#define x x[0]
#define y(arg) arg

y(x)

This invocation expands to x[0] (tested on Visual C++ 2010, g++ 4.1, mcpp 2.7.2, and Wave).

Why? Specifically, why does it not expand to x[0][0]?

During macro replacement,

A parameter in the replacement list...is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced (C++03 §16.3.1/1).

Evaluating the macro invocation, we take the following steps:

  • The function-like macro y is invoked with x as the argument for its arg parameter
  • The x in the argument is macro-replaced to become x[0]
  • The arg in the replacement list is replaced by the macro-replaced value of the argument, x[0]

The replacement list after substitution of all the parameters is x[0].

After all parameters in the replacement list have been substituted, the resulting preprocessing token sequence is rescanned...for more macro names to replace (C++03 §16.3.4/1).

If the name of the macro being replaced is found during this scan of the replacement list...it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced (C++03 §16.3.4/2).

The replacement list x[0] is rescanned (note that the name of the macro being replaced is y):

  • x is identified as an object-like macro invocation
  • x is replaced by x[0]

Replacement stops at this point because of the rule in §16.3.4/2 preventing recursion. The replacement list after rescanning is x[0][0].

I have clearly misinterpreted something since all of the preprocessors I've tested say I am wrong. In addition, this example is a piece of a larger example in the C++0x FCD (at §16.3.5/5) and it too says that the expected replacement is x[0].

Why is x not replaced during rescanning?

C99 and C++0x effectively have the same wording as C++03 in the quoted sections.

+15  A: 

I believe you've quoted the crucial paragraph, you just stopped too soon. 16.3.4/2 (emphasis mine):

If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.

So, when x is replaced with x[0] during the parameter substitution of y, it is fully macro replaced, meaning that it is rescanned at that point, and x is caught by the recursion rule. This means that the x in x[0] is no longer eligible for further replacement, including during the rescanning of the partly-expanded result of y(x).

Steve Jessop
Ah ha! I forgot that the replacement list of `x` would be rescanned before it was substituted; now it all makes sense. Thank you, Steve.
James McNellis