6

Why does the following code compiles?

#ifdef C++11
// ...
#endif

int main() {}

gcc 4.8.0 gives me the following warning:

extra tokens at end of #ifdef directive

According to the standard, the macro name can contain only letters, digits and underscore character.

Maybe because this?

ISO/IEC 14882:2011

16.1 Conditional inclusion [cpp.cond]

6 Each directive’s condition is checked in order. If it evaluates to false (zero), the group that it controls is skipped: directives are processed only through the name that determines the directive in order to keep track of the level of nested conditionals; the rest of the directives’ preprocessing tokens are ignored, as are the other preprocessing tokens in the group. Only the first group whose control condition evaluates to true (nonzero) is processed. If none of the conditions evaluates to true, and there is a #else directive, the group controlled by the #else is processed; lacking a #else directive, all the groups until the #endif are skipped.151

I can't understand this quote correctly.

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242
  • I believe preprocessor identifiers follow the same rules as variable identifiers, though upper-case is preferred. The `C++11` doesn't work. This SO question should help you: http://stackoverflow.com/questions/10717502/is-there-a-preprocessor-directive-for-detecting-c11x-support – Eric Jablow Apr 10 '13 at 20:01
  • @Eric Jablow I know about __cplusplus macro, i want to know why this code compiles in gcc 4.8.0, clang 3.2, icc 13.0.1 and MSVC-11 – FrozenHeart Apr 10 '13 at 20:26
  • 1
    Sorry. I should have read better. Perhaps GCC is too forgiving. – Eric Jablow Apr 10 '13 at 20:40

2 Answers2

4

As far as C++ is concerted, #ifdef C++11 is a syntax error. There is no rule saying a compiler has to reject a program with a syntax error.

1.4 Implementation compliance [intro.compliance]

The set of diagnosable rules consists of all syntactic and semantic rules in this International Standard except for those rules containing an explicit notation that "no diagnostic is required" or which are described as resulting in "undefined behavior."

[...]

If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as "conditionally-supported" when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.

A warning is a diagnostic message. The compilers are perfectly within their rights to continue to successfully compile the program, as long as they ensure they show you that one diagnostic message. Since compilers have historically accepted such directives, and accepting such directives does not conflict with the requirements of the standard, they continue to do so.

At least as far as GCC is concerned, you can ask to make all standard-required diagnostics a hard error with the -pedantic-errors option.

$ printf "#ifdef C++11\n#endif\n" | gcc -std=c++11 -pedantic-errors -E -x c++ -
# 1 "<stdin>"
# 1 "<command-line>"
# 1 "<stdin>"
<stdin>:1:9: error: extra tokens at end of #ifdef directive
Community
  • 1
  • 1
1

A #ifdef is defined as follow (taken from §16.1)

# ifdef      identifier new-line

With regexp-like notation, an identifier is: [a-zA-Z_][a-zA-Z_0-9]* (*)

The point is: the macro you declare is NOT C++11. It is in fact C (see this live example). The ++11 part is ignored by the preprocessor. The only allowed character after the identifier (which is C) is a new-line, but as said in hvd's answer, from §1.4, a syntax error only force a diagnostic message, here the warning; the only reason I see for this instead of an error is to be compatible with old code, where such names sould have been used.

Also: the quote explains how #ifdef / #elif / #else / #endif work together, not the way conditions are specified.

I do not have a copy of the standard. I used draft n3485 for this answer.

(*) It is possible to have implementation-defined characters in an identifier, but that does not impact your question. Note that variables, class name, macros, ... all follows the same identifier rules.

Synxis
  • 9,236
  • 2
  • 42
  • 64
  • "I think this is not standard compliant" Are you sure? gcc 4.8.0, clang 3.2, icc 13.0.1 and MSVC-11 compiles this code – FrozenHeart Apr 10 '13 at 20:48
  • Yes, but it does not mean this is standard. gcc, clang are not the standard, they try to *follow* it. If they were standard-compliant (on this point), they would ouput an error, not a warning (unless I am missing something). – Synxis Apr 10 '13 at 20:49
  • And what do you mean by "However, that is not the cause"? I think that this quote from standard allows + in the macro names, so C++11 can be valid macro name – FrozenHeart Apr 10 '13 at 20:51
  • 1
    Yes, I thought that when seeing the 'implementation defined' part. However, the identifier is not `C++11` but `C`, meaning that `+` is not part of *`identifier-nondigit`*, that's why I don't think it is why your code is accepted. – Synxis Apr 10 '13 at 20:53
  • "However, the identifier is not C++11 but C" Are you talking about gcc and clang behavior? Can C++11 be a valid macro name in the other compilers according to this quote from the standard? Am i correctly understand it? – FrozenHeart Apr 10 '13 at 20:55
  • I am not totally sure. I did not find what is meant by the comittee with `other implementation defined characters`. Remember that *`identifier`* is also used for variable names, etc... Macro names follow the same name rules than variables, and you cannot put a `+` in a variable name - obviously because of ambiguities ;) – Synxis Apr 10 '13 at 20:57
  • "other implementation-defined characters" are for example `$`. Definitely not `+`, that would cause problems for valid code such as `int a = 1; int b = 2; return a+b;` –  Apr 10 '13 at 21:09
  • @hvd Ok, that is what I thought. So, it seems my answer is a little bloated with the *`identifier`* part, I will remove it. – Synxis Apr 10 '13 at 21:12
  • `#define C++11` is valid; it defines the macro `C` with the expansion `++11`. `#ifdef C++11` is not. – Keith Thompson Apr 10 '13 at 21:30
  • I didn't say anything about `#define C++11`. I said that the defined macro was `C`. We agree on the fact that `#ifdef C++11` is not valid. – Synxis Apr 10 '13 at 21:32
  • 1
    @KeithThompson Even that isn't valid: "There shall be white-space between the identifier and the replacement list in the definition of an object-like macro." –  Apr 10 '13 at 21:33
  • @Synxis: I see `#define c++11` in [your example](http://liveworkspace.org/code/460h5Z%240). It's quite possible that a compiler that accepts that with a warning will treat `c` as the macro name -- but since it's a syntax error, we can't in general say that it defines a macro at all. – Keith Thompson Apr 10 '13 at 21:43
  • @KeithThompson It was to better "demonstrate" the fact that the defined macro was not `C++11`. It is a syntax error, I know. Gcc only outputs a warning and continue parsing the code. The "why" is explained in hvd's answer. – Synxis Apr 10 '13 at 21:47