11

Given

#define cat(x,y) x##y

The call cat(a,1) returns a1, but cat(cat(1,2),3) is undefined. However if I also define #define xcat(x,y) cat(x,y), then the result of xcat(xcat(1,2),3) is now 123. Can anybody please explain in detail why this is so?

Jay Sullivan
  • 17,332
  • 11
  • 62
  • 86
sourabh912
  • 669
  • 3
  • 7
  • 14

3 Answers3

2

I tested this using both GCC and Clang.

GCC gives the error:

test.c:6:1: error: pasting ")" and "3" does not give a valid preprocessing token

Clang gives the error:

test.c:6:11: error: pasting formed ')3', an invalid preprocessing token
  int b = cat(cat(1,2),3);

What appears to be happening is that the compiler wraps the result of cat(1,2) in parentheses as soon as it is expanded; so when you call cat(1,2) in your code, it really gives you (12). Then, calling cat((12),3) again leads to ((12)3), which is not a valid token, and this results in a compile error.

The common opinion is "when using the token-pasting operator (##), you should use two levels of indirection" (i.e., use your xcat workaround). See Why do I need double layer of indirection for macros? and What should be done with macros that need to paste two tokens together?.

Community
  • 1
  • 1
Jay Sullivan
  • 17,332
  • 11
  • 62
  • 86
  • In my question after substituting xcat(x,y) with cat(x,y) it again becomes cat(cat(1,2),3) then even now it should return invalid token. I am unable to understand how the two levels of indirection is working. I read the above URL but still it is not very clear to me. – sourabh912 Jun 14 '12 at 18:35
1

In xcat(x,y), the x and y are not adjacent to the ## operator, and so they undergo macro expansion before being substituted.

So x is identified as xcat(1,2) and y is identified as 3. But prior to substitution, x is macro-expanded to cat(1,2), which turns into 1##2 which turns into 12. So ultimately, xcat(xcat(1,2),3) will expand to cat(12,3), which will turn out 123.

This Works --> cat(xcat(1,2),3) --> cat(cat(1,2),3) --> cat(12,3)

The behavior is well-defined because all of the token pastings result in valid preprocessor tokens i.e any expanded xpression should be a valid token at any stage.

Koder101
  • 844
  • 15
  • 28
0

I don't think if cat is actually gonna be expanded for 2 consecutive times. That's why I wonder why would compiler even produce such a message like 'pasting ")" and "3" does not give a valid preprocessing token'. Also, I don't think the inner cat is gonna be expanded first. So, I presume the output would be cat(1,2)3. That direct me to cogitate how would the compiler interpret this.

sschrass
  • 7,014
  • 6
  • 43
  • 62
dspjm
  • 5,473
  • 6
  • 41
  • 62