#ifdef vs #if - which is better/safer as a method for enabling/disabling compilation of particular sections of code?

105,978

Solution 1

My initial reaction was #ifdef, of course, but I think #if actually has some significant advantages for this - here's why:

First, you can use DEBUG_ENABLED in preprocessor and compiled tests. Example - Often, I want longer timeouts when debug is enabled, so using #if, I can write this

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... instead of ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

Second, you're in a better position if you want to migrate from a #define to a global constant. #defines are usually frowned on by most C++ programmers.

And, Third, you say you've a divide in your team. My guess is this means different members have already adopted different approaches, and you need to standardise. Ruling that #if is the preferred choice means that code using #ifdef will compile -and run- even when DEBUG_ENABLED is false. And it's much easier to track down and remove debug output that is produced when it shouldn't be than vice-versa.

Oh, and a minor readability point. You should be able to use true/false rather than 0/1 in your #define, and because the value is a single lexical token, it's the one time you don't need parentheses around it.

#define DEBUG_ENABLED true

instead of

#define DEBUG_ENABLED (1)

Solution 2

They're both hideous. Instead, do this:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

Then whenever you need debug code, put it inside D();. And your program isn't polluted with hideous mazes of #ifdef.

Solution 3

#ifdef just checks if a token is defined, given

#define FOO 0

then

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

Solution 4

We have had this same problem across multiple files and there is always the problem with people forgetting to include a "features flag" file (With a codebase of > 41,000 files it is easy to do).

If you had feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

But then You forgot to include the header file in file.cpp:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

Then you have a problem, the compiler interprets COOL_FEATURE being undefined as a "false" in this case and fails to include the code. Yes gcc does support a flag that causes a error for undefined macros... but most 3rd party code either defines or does not define features so this would not be that portable.

We have adopted a portable way of correcting for this case as well as testing for a feature's state: function macros.

if you changed the above feature.h to:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

But then you again forgot to include the header file in file.cpp:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

The preprocessor would have errored out because of the use of an undefined function macro.

Solution 5

For the purposes of performing conditional compilation, #if and #ifdef are almost the same, but not quite. If your conditional compilation depends on two symbols then #ifdef will not work as well. For example, suppose you have two conditional compilation symbols, PRO_VERSION and TRIAL_VERSION, you might have something like this:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

Using #ifdef the above becomes much more complicated, especially getting the #else part to work.

I work on code that uses conditional compilation extensively and we have a mixture of #if & #ifdef. We tend to use #ifdef/#ifndef for the simple case and #if whenever two or more symbols are being evaluation.

Share:
105,978
Jon Cage
Author by

Jon Cage

A Computer Systems Engineer based in the UK

Updated on July 20, 2021

Comments

  • Jon Cage
    Jon Cage almost 3 years

    This may be a matter of style, but there's a bit of a divide in our dev team and I wondered if anyone else had any ideas on the matter...

    Basically, we have some debug print statements which we turn off during normal development. Personally I prefer to do the following:

    //---- SomeSourceFile.cpp ----
    
    #define DEBUG_ENABLED (0)
    
    ...
    
    SomeFunction()
    {
        int someVariable = 5;
    
    #if(DEBUG_ENABLED)
        printf("Debugging: someVariable == %d", someVariable);
    #endif
    }
    

    Some of the team prefer the following though:

    // #define DEBUG_ENABLED
    
    ...
    
    SomeFunction()
    {
        int someVariable = 5;
    
    #ifdef DEBUG_ENABLED
        printf("Debugging: someVariable == %d", someVariable);
    #endif
    }
    

    ...which of those methods sounds better to you and why? My feeling is that the first is safer because there is always something defined and there's no danger it could destroy other defines elsewhere.

  • tloach
    tloach over 15 years
    This fails, since #define DEBUG=0 will now not run #if but will run #ifdef
  • Don Neufeld
    Don Neufeld over 15 years
    That's a lousy solution, it has the following problems: 1. Only works in functions, you can't remove unneeded class variables, etc 2. Compilers may throw warnings about unreachable code 3. Code in the if still needs to compile, which means you have to keep all your debug functions defined, etc.
  • Pigsty
    Pigsty over 15 years
    First the question was specifically about debug printfs, so unneeded class variables are not an issue here. Second, given the capabilities of modern compilers you should use #ifdefs as little as possible. In most cases you can use build configurations or template specializations instead.
  • Michael Carman
    Michael Carman over 15 years
    The constant might not be used to enable/disable debugging, so triggering a #ifdef with a #define to 0 could be not so benign. As for true/false, those were added in C99 and don't exist in C89/C90.
  • Martin Beckett
    Martin Beckett over 15 years
    Thats the point, I can either remove DEBUG completely or just set it to 0 to disable it.
  • Jon Cage
    Jon Cage over 15 years
    I agree, and we actually do that in our code, I just wanted an example of something you might use #if etc. for
  • Jon Cage
    Jon Cage over 15 years
    I agree that macros can be dangerous, but that first example would be fairly obvious to debug and of course it only gives one error. Why would you expect more? I've seen much nastier errors as a result of macros...
  • Jon Cage
    Jon Cage over 15 years
    Personally I think it's easier to miss that little exclamation mark!
  • Jon Cage
    Jon Cage over 15 years
    Perhaps the printing macro wasn't the best example afterall - we actually do this already in our codebase for our more standard debug code. We use the #if / #ifdefined bits for areas which you might want to turn on extra debugging..
  • Jon Cage
    Jon Cage over 15 years
    Any particular reason (out of curiosity)?
  • Jon Cage
    Jon Cage over 15 years
    Micheal: He/she was advocating against the use of #ifdef ?!
  • tloach
    tloach over 15 years
    To be honest I never thought about it - just how it's been done places I've worked. It does give the advantage that instead of making a code change for a production build all you have to do is 'make DEBUG' for debug, or 'make PRODUCTION' for regular
  • Jim Buck
    Jim Buck over 15 years
    With syntax highlighting? :) In syntax highlighting, the "n" in "ifndef" is much harder to spot since it's all the same color.
  • Jon Cage
    Jon Cage over 15 years
    Okay I meant #ifndef is easier to spot than #if !defined when you're comparing against #if defined .. but given all #if defined/#if !defined vs #ifdef/#ifndef, either is equally miss-readable!
  • paercebal
    paercebal over 15 years
    It's true the difference between one solution and another is almost trivial. But in this case, as we are talking about two competing coding styles, then even the trivial can't be ignored, because after that, all that is left is personal taste (and at that point, I believe it shouldn't be normalized)
  • shank
    shank almost 14 years
    The OP wasn't asking for which is better between "#ifdef" and "#if defined" but rather between "#ifdef/#if defined" and "#if".
  • detly
    detly almost 14 years
    @Jon Cage - what platform? Some of them do, but in strange places.
  • Keshava GN
    Keshava GN about 11 years
    it should be #define DEBUG 1 . Not #define DEBUG=1
  • bames53
    bames53 almost 11 years
    Yeah, one problem with #ifdef is that it works with things that aren't defined; whether they're not defined intentionally or because of a typo or what have you.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 11 years
    Your addition to the answer is wrong. #if DEBUG_ENBALED is not an error detected by the preprocessor. If DEBUG_ENBALED is not defined, it expands to the token 0 in #if directives.
  • Will
    Will over 10 years
    @R.. In many compilers you can enable a warning for "#if DEBUG_ENABLED" when DEBUG_ENABLED is not defined. In GCC use "-Wundef". In Microsoft Visual Studio use "/w14668" to turn on C4668 as a level 1 warning.
  • nmxprime
    nmxprime about 10 years
    in #if defined what is defined is it a key word or ?
  • Casey Kuball
    Casey Kuball over 9 years
    @MatthieuM. Actually, I think the original version was fine. The semicolon would be interpreted as an empty statement. However, forgetting the semicolon could make it dangerous.
  • Pharap
    Pharap about 8 years
    @JonCage I know it's been a few years since this comment but I'd like to point out you can write it as #if ! defined to make the ! more prominent and hard to miss.
  • Jon Cage
    Jon Cage about 8 years
    @Pharap - That certainly looks like an improvement :)