3

Why does the following error occur?

#include <iostream>

#define concatenate(t1, t2) t1 ## t2 // Concatenate token1 and token2

int main()
{
    int myVar = 22;
    std::cout << concatenate(my, Var); // Compiles fine and outputs the value of myVar

    concatenate(/, /) So I thought that this would be a comment but this is a compile-time error
    // error: pasting "/" and "/" does not give a valid preprocessing token

    return 0;
}

I thought that concatenate(/, /) would tell the preprocessor to replace it with a // and then as it parses further, that entire line would be interpreted as a comment.

What is actually happening in this case?

Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
  • 1
    Does this answer your question? [Does the C preprocessor strip comments or expand macros first?](https://stackoverflow.com/questions/1510869/does-the-c-preprocessor-strip-comments-or-expand-macros-first) – Dai Jan 12 '20 at 13:10
  • 1
    The compiler removes comments such as `//` and `/* ... */`, so sending that in to a macro is not going to be interpreted as such. – Moo-Juice Jan 12 '20 at 13:10

2 Answers2

7

This answer is for C, it's similar in C++.

The example is literally the same as in C11 standard 6.4.9p3:

      #define glue(x,y) x##y
      glue(/,/) k();                     // syntax error, not comment

The error you are seeing:

error: pasting "/" and "/" does not give a valid preprocessing token

comes from that the result of ## needs to be preprocessing tokens. In short, a preprocessing token are identifiers, preprocessing numbers, string literals, punctuators, and other. (See also gcc docs on tokenization). The resulting // string is not a preprocessing token, so the result of ## here would not be a preprocessing token. The C standard 6.10.3.3p3 states that if a result of ## "is not a valid preprocessing token, the behavior is undefined". The compiler you are using chooses to issue an error in such case. It will not work the same way as the following do not work:

concatenate(:, b) // error, `:b` is not a preprocessing token
concatenate(%, b) // error, `%b` is not a preprocessing token
// etc.

Maybe for example from the other side, for example %= is a valid token, a punctuator. The following are ok:

concatenate(%, =)
concatenate(/, =)
concatenate(a /, = b)
concatenate(<, :)

Anyway, even if // would be a valid preprocessing, comments are substituted for a single space in translation phase 3, while the preprocessor is executed in translation phase 4 after comments are removed, see C11 translation phases. So, even if it would result in // token, it would be invalid, as it doesn't mean anything in C (except comments).

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Excellent explaination, +1! However, it raises another question for me: if token pasting is illegal for forward slashes, why have I seen such things as `#if 0 #define DbgPrint /##/ #else ...`? – Kenny83 Nov 07 '20 at 20:15
  • I cannot give you an explanation why you have seen something... Inside `#if 0` anything is allowed. – KamilCuk Nov 07 '20 at 22:47
  • LOL I didn't literally mean "why have I seen this?", rather "how is this possible and what does it mean?". I get that `#if 0 ...` means that code can never possibly be processed, and I'm guessing the dev in question was creating an easily-commentable block of code for testing/debugging, but my question really just boils down to this: how does the token pasting part allow that to work? If you don't know, fair enough. Sorry to bother you :-) – Kenny83 Nov 07 '20 at 23:29
  • I do not understand. No, It doesn't, and this answer explains why and why it's not possible for macro to be a comment (last paragraph). Also ## requires the arguments to be macro arguments. Please post an [mcve]. – KamilCuk Nov 08 '20 at 08:43
3

For C++:

## must result in a valid preprocessing token.

// is not considered a preprocessing token. Instead comments, including the // introducer, are considered white space, see [lex.token]/1 of the C++17 standard (final draft).

If ## does not generate a valid preprocessing token, such as here, the program has undefined behavior. See [cpp.concat]/3. This means that the compiler wont even be required to tell with an error message that you messed up.

All comments are removed from the source file before preprocessor directives, such as macro definition and replacement, are executed, so even if you could generate a // token, it wouldn't be replaced and would be a syntax error. See [lex.phases]/1.3-1.4

walnut
  • 21,629
  • 4
  • 23
  • 59