29

I occasionally write code something like this:

// file1.cpp
#define DO_THIS 1

#if DO_THIS
    // stuff
#endif

During the code development I may switch the definition of DO_THIS between 0 and 1.

Recently I had to rearrange my source code and copy some code from one file to another. But I found that I had made a mistake and the two parts had become separated like so:

// file1.cpp
#define DO_THIS 1

and

// file2.cpp
#if DO_THIS
    // stuff
#endif

Obviously I fixed the error, but then thought to myself, why didn't the compiler warn me? I have the warning level set to 4. Why isn't #if X suspicious when X is not defined?

One more question: is there any systematic way I could find out if I've made the same mistake elsewhere? The project is huge.

EDIT: I can understand having no warning with #ifdef that makes perfect sense. But surely #if is different.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Mick
  • 8,284
  • 22
  • 81
  • 173
  • 4
    Remember that this is defined behavior (an undefined preprocessing symbol has a value of 0), and that it's very likely that a multiplatform program will mention a lot of preprocessor macros that aren't defined. – David Thornley Oct 29 '09 at 14:03
  • 1
    Good question, thanks for posting it. – ChipJust Aug 07 '15 at 20:21
  • Very interesting question, but it seems no answer answers "why" in the question title even in 2020 now. I visited this page to know "why" (i.e. the original intention of the standard committee) ... – ynn Apr 12 '20 at 22:32

8 Answers8

41

gcc can generate a warning for this, but its probably not required by the standard:

-Wundef
Warn if an undefined identifier is evaluated in an `#if' directive.

Bill
  • 14,257
  • 4
  • 43
  • 55
19

Again, as it often happens, the answer to the "why" question is just: it was done that way because some time ago it was decided to do it this way. When you use an undefined macro in an #if it is substituted with 0. You want to know whether it is actually defined - use defined() directive.

There some interesting benefits to that "default to 0" approach though. Especially when you are using macros that might be defined by the platform, not your own macros.

For example, some platforms offer macros __BYTE_ORDER, __LITTLE_ENDIAN and __BIG_ENDIAN to determine their endianness. You could write preprocessor directive like

#if __BYTE_ORDER == __LITTLE_ENDIAN
  /* whatever */
#else
  /* whatever */
#endif

But if you try to compile this code on a platform that does not define these non-standard macros at all (i.e. knows nothing about them), the above code will be translated by preprocessor into

#if 0 == 0
...

and the little-endian version of the code will be compiled "by default". If you wrote the original #if as

#if __BYTE_ORDER == __BIG_ENDIAN
...

then the big-endian version of the code would be compiled "by default".

I can't say that #if was defined as it was specifically for tricks like the above, but it comes useful at times.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • "use defined() directive"... defined()? I could not find that on msdn. – Mick Oct 29 '09 at 14:10
  • 2
    I'm talking about `#if defined()`. In MSDN it should be under `#if`. Maybe it is properly called "operator" , not "directive". – AnT stands with Russia Oct 29 '09 at 14:12
  • 1
    @Bill: I mean specifically operator `defined(...)`. `#if defined(...)` has an equivalent shorter form `#ifdef`, but if you need a more complex constrolling expression with logical operators, then `defined(...)` is the way to go. – AnT stands with Russia Oct 29 '09 at 16:30
  • Standard quote: https://stackoverflow.com/questions/5085392/what-is-the-value-of-an-undefined-constant-used-in-if/5085425#5085425 – Ciro Santilli OurBigBook.com Oct 16 '17 at 13:59
  • "When you use an undefined macro in an #if it is substituted with 0" - I looked for a source, and this is specified in section 6.10.1 of C99, C11 and C17. – AJM May 30 '22 at 16:10
1

When you can't use a compiler that has a warning message (like -Wundef in gcc), I've found one somewhat useful way to generate compiler errors.

You could of course always write:

#ifndef DO_THIS
    error
#endif
#if DO_THIS

But that is really annoying

A slightly less annoying method is:

#if (1/defined(DO_THIS) && DO_THIS)

This will generate a divide by zero error if DO_THIS is undefined. This method is not ideal because the identifier is spelled out twice and a misspelling in the second would put us back where we started. It looks weird too. It seems like there should be a cleaner way to accomplish this, like:

#define PREDEFINED(x) ((1/defined(x)) * x)
#if PREDEFINED(DO_THIS)

but that doesn't actually work.

  • For the record I have had some success with `#define PREDEFINED(opt) (1 / defined opt## && opt)`. The concatenation is there to prevent expansion but to be honest I don't understand why the lack of parenthesis around the define operator should affect the evaluation. – doynax May 16 '15 at 20:56
1

If you're desperate to prevent this kind of error, try the following which uses preprocessor token-pasting magic and expression evaluation to enforce that a macro is defined (and 0 in this example):

#define DEFINED_VALUE(x,y) (defined (y##x) ? x : 1/x)
#if DEFINED_VALUE(FEATURE1,) == 0
Steven Kramer
  • 8,473
  • 2
  • 37
  • 43
1

There is recursive issue. In case you have

#define MODEL  MODEL_A

#if (MODEL == MODEL_B)
 // Surprise, this is compiled!
#endif

where definition of MODEL_A and MODEL_B are missing, then it will compile.

#ifdef MODEL
#error Sorry, MODEL Not Defined
// Surprise, this error is never reached (MODEL was defined by undefined symbol!)
#endif 

#ifdef MODEL_B
#error Sorry, MODEL_B Not Defined
// This error is reached
#endif 
Milos
  • 11
  • 2
1

If DO_THIS is yours definition then simple and working solution seems to be usage of Function-like Macro:

#define DO_THIS() 1

#if DO_THIS()
    //stuff
#endif

I tested this under Visual Studio 2008, 2015 and GCC v7.1.1. If DO_THIS() is undefined VS gererates:

warning C4067: unexpected tokens following preprocessor directive - expected a newline

and GCC generates

error: missing binary operator before token "("

dczarnec
  • 11
  • 2
0

The compiler didn't generate a warning because this is a preprocessor directive. It's evaluated and resolved before the compiler sees it.

Greg D
  • 43,259
  • 14
  • 84
  • 117
  • 3
    This is humerous as a comment - but not really very helpful as an answer! 99.9% of the time, people will say compiler and mean everything from preprocessing to linking....and it's reasonable when you think that a single call to something like 'g++' does it all! – Richard Corden Oct 30 '09 at 17:27
  • @Richard: This answer is actually kinda' tongue-in-cheek. I was cracking wise about how often I've seen answers (in the C++ community at large) that are technically correct, yet utterly without value. ;) If I really wanted to take the joke to the nth degree, I would also have included a couple references to the C++ standard. – Greg D Oct 30 '09 at 19:20
0

If I'm thinking about this correctly.

Preprocessor directives are handled before any source code is compiled. During that phase(s) of translation in which this occurs all preprocessor directives, macros, etc are handled and then the actual source code is compiled.

Since #if is used to determine if X has been defined and carry out some action if it has or has not been defined. The #if in the code snippet would compile without any errors because there aren't any errors as far as the compiler is concerned. You could always create a header file with specific #defines that your application would need and then include that header.

ChadNC
  • 2,528
  • 4
  • 25
  • 39