Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

About ## preprocessor in C

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?

like image 620
sourabh912 Avatar asked Jun 14 '12 16:06

sourabh912


2 Answers

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?.

like image 93
Jay Sullivan Avatar answered Oct 13 '22 16:10

Jay Sullivan


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.

like image 27
Koder101 Avatar answered Oct 13 '22 18:10

Koder101