1

I've distilled my problem down to this code snippet - but it is a part of a larger program so I don't want a different way to do this - I need a way to make this work!

When I generate a preprocessed file from this code:

#define OUTER(a, b) \
    a##b
#define INNER(c, d) \
    c##d

enum foo {
    OUTER(INNER(x, y), z)
}; // line 108

int APIENTRY _tWinMain(...)
{
    foo bar = xyz; // line 112
}

I get:

enum foo {
    xyz
}; // line 108

int __stdcall wWinMain(...)
{
    foo bar = xyz; // line 112
}

which is what I want. However, if I try to compile the code I get:

error C2146: syntax error : missing '}' before identifier 'z' line 108
error C2143: syntax error : missing ';' before '}' line 108
error C2143: syntax error : missing ';' before '}' line 108
error C2059: syntax error : '}' line 108
error C2065: 'xyz' : undeclared identifier line 112

I can't work it out! The problem seems to be caused by the ## in the:

#define OUTER(a, b) \
    a##b

but why (and how to fix it) is beyond me...

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jack
  • 13
  • 2
  • @Jack Could you explain what a##b is supposed to do? Never saw this before. – InsertNickHere Aug 01 '10 at 17:45
  • 1
    @Insert It's a preprocessor operator that literally concatenates its two arguments. For instance, `OUTER(test, string)` in the OP code snippet is replaced by `teststring` by the preprocessor. – Mehrdad Afshari Aug 01 '10 at 17:47
  • OK well I can confirm that this happens in gcc too, and their error is a bit more descriptive. It's saying that it can't paste `)` and `z` together. It looks to me like the preprocessor can't do nested pastes like this, but this is guesswork at this point. Hope that helps a bit.. – Blindy Aug 01 '10 at 17:48
  • @InsertNickHere: http://msdn.microsoft.com/en-us/library/09dwwt6y%28VS.80%29.aspx It is the Token-Pasting Operator – abelenky Aug 01 '10 at 17:48
  • 1
    @InsertNickHere It's token concatenation, part of any C preprocessor. http://en.wikipedia.org/wiki/C_preprocessor#Token_concatenation – nos Aug 01 '10 at 17:49
  • 1
    Can you provide a more realistic example of usage or the macros? Right now, the macros are essentially useless. – strager Aug 01 '10 at 17:50

3 Answers3

9

Use this instead:

#define CONCAT(X,Y) X##Y
#define OUTER(a, b) CONCAT(a,b)
#define INNER(a, b) CONCAT(a,b)

enum foo {
    OUTER(INNER(x, y),z)
}; // line 108

int main(...)
{
    foo bar = xyz; // line 112
}
Nordic Mainframe
  • 28,058
  • 10
  • 66
  • 83
  • Woo hoo it works! But can you explain to me why it works? Why is my preprocessed output correct? I see from the gcc error described below that it appears to be keeping a ")" and trying to concatenate the "z" to "xy)"? But this doesn't appear in my preprocessed output! – Jack Aug 01 '10 at 17:59
  • Read Jonathan Leffler's canonical answer: http://stackoverflow.com/questions/1489932/c-preprocessor-and-concatenation – Nordic Mainframe Aug 01 '10 at 18:04
  • Ah! Is OUTER getting "INNER(c, d), z" and not "xy, z" as I thought? That would make sense. But I still don't get why my preprocessed output is correct! – Jack Aug 01 '10 at 18:05
  • No need to declare two separate OUTER/INNER macros, this works also: OUTER(OUTER(x, y),z) – Agnius Vasiliauskas Aug 28 '10 at 07:47
2

Preprocessing your example with gcc results in:

enum foo {
t.c:7:1: error: pasting ")" and "z" does not give a valid preprocessing token
    xy z
};

which should give you a clue of why Luther's solution works and yours doesn't.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
1

If you are using gcc, then you can give it the -E option to see the preprocessed output. Then you can easily see what the preprocessor has output and how to further debug your macros. Other compilers also have similar options.

zvrba
  • 24,186
  • 3
  • 55
  • 65