#ifdef with multiple tokens, is this legal?
Asked Answered
A

2

6

Today I came across some C++ code that contains an #ifdef clause like this:

#ifdef DISABLE_UNTIL OTHER_CODE_IS_READY
   foo();
#endif

Note the space between "DISABLE_UNTIL" and "OTHER_CODE_IS_READY". Essentially there are two tokens specified in the #ifdef line.

My question is, is this legal C++ code? (g++ compiles it without any errors, and it apparently just ignores the second token). And if it is legal, should the second token have any effect?

Almaalmaata answered 11/10, 2013 at 15:32 Comment(8)
Every version of g++ I've tried warns about this, from 2.8.1 to 4.8.1, warns about this, either "warning: garbage at end of '#ifdef' argument", "warning: extra tokens at end of #ifdef directive", or "warning: extra tokens at end of #ifdef directive [enabled by default]". How did you not get a diagnostic?Coadjutor
@Keith: g++ myprogram.cpp -o myprogram | grep ! warningCordilleras
@LightnessRacesinOrbit: That's not going to filter out warnings (the grep syntax is wrong, and gcc writes diagnostics to stderr). In any case, I think you're speculating; I'd like to know how the OP didn't get a warning. OP: Did you intentionally disable warnings?Coadjutor
@KeithThompson I just wrote a 5-line test program and compiled it with: g++ temp.cpp (using i686-apple-darwin11-llvm-g++-4.2 under MacOS/X 10.8.5)Almaalmaata
What 5-line program? Are you saying there were no warnings?Coadjutor
@KeithThompson Yes, the following compiles without warnings on my machine: #include <stdio.h> int main(int, char **) { #ifdef DISABLE_UNTIL OTHER_CODE_IS_READY foo(); #endif return 0; }Almaalmaata
(You'll have to reinsert newlines at the proper places; I don't think there is a way to post code properly in a comment, sorry :( )Almaalmaata
@JeremyFriesner: That's very odd. With g++ 4.7.2, I get "warning: extra tokens at end of #ifdef directive". With clang++ 3.0, I get "warning: extra tokens at end of #ifdef directive". In any case, any conforming compiler should diagnose an extra token on an #ifdef.Coadjutor
C
6

[C++11 16.1], [C++11 16.5] and, incidentally, [C99 6.10.1/4] all say that this is invalid.

if-group:
# if constant-expression new-line groupopt
# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt

Only one identifier is legal.

GCC's own documentation agrees.

My own tests suggest that only the first identifer is accepted, and the second is simply discarded; this may be to ease the implementation, but the standard does require a diagnostic here, so you should see this when you use the -pedantic flag at least.

#include <iostream>
using namespace std;

#define A
#define B

int main() {
    #ifdef A
    std::cout << "a ";
    #endif

    #ifdef B
    std::cout << "b ";
    #endif

    #ifdef C
    std::cout << "c ";
    #endif

    #ifdef B C
    std::cout << "bc ";
    #endif

    #ifdef C B
    std::cout << "cb ";
    #endif

    return 0;
}

// Output: "a b bc"
// Note: "cb" *not* output

Coliru's installation of GCC emits it with or without -pedantic.

Cordilleras answered 11/10, 2013 at 15:33 Comment(2)
"and certainly the standard doesn't outright require a diagnostic here' -- The standard requires a diagnostic for any violation of a syntax rule.Coadjutor
@Keith: Oh yes, you're right ([C++11: 1.4/2]). I'd always thought of diagnostics as opt-in "by default" for standard rules, whereas in fact the opposite is largely true. Thanks.Cordilleras
I
14

The syntax you posted is not legal, and the intended meaning is unclear.

Depending on what you hope to accomplish, can you use || or && to combine them?
(of course if this is someone else's code, I'd just reject it as inappropriate / unusable)

#if defined(DISABLE_UNTIL) || defined(OTHER_CODE_IS_READY)
    foo();
#endif
Illfavored answered 11/10, 2013 at 15:35 Comment(4)
You answered some other question very well.Cordilleras
And how does this answers the question "if it is legal" ?Baptiste
I'm not sure what the author of the code was hoping to accomplish... I suspect it was just a typo (he accidentally typed a space rather than an underbar).Almaalmaata
+1 since I find the answer useful even if it doesn't answer the "is this legal?" part of the question. But what is wrong with this code for you to "reject it as inappropriate / unusable"?Embassy
C
6

[C++11 16.1], [C++11 16.5] and, incidentally, [C99 6.10.1/4] all say that this is invalid.

if-group:
# if constant-expression new-line groupopt
# ifdef identifier new-line groupopt
# ifndef identifier new-line groupopt

Only one identifier is legal.

GCC's own documentation agrees.

My own tests suggest that only the first identifer is accepted, and the second is simply discarded; this may be to ease the implementation, but the standard does require a diagnostic here, so you should see this when you use the -pedantic flag at least.

#include <iostream>
using namespace std;

#define A
#define B

int main() {
    #ifdef A
    std::cout << "a ";
    #endif

    #ifdef B
    std::cout << "b ";
    #endif

    #ifdef C
    std::cout << "c ";
    #endif

    #ifdef B C
    std::cout << "bc ";
    #endif

    #ifdef C B
    std::cout << "cb ";
    #endif

    return 0;
}

// Output: "a b bc"
// Note: "cb" *not* output

Coliru's installation of GCC emits it with or without -pedantic.

Cordilleras answered 11/10, 2013 at 15:33 Comment(2)
"and certainly the standard doesn't outright require a diagnostic here' -- The standard requires a diagnostic for any violation of a syntax rule.Coadjutor
@Keith: Oh yes, you're right ([C++11: 1.4/2]). I'd always thought of diagnostics as opt-in "by default" for standard rules, whereas in fact the opposite is largely true. Thanks.Cordilleras

© 2022 - 2024 — McMap. All rights reserved.