Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)

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.


#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"

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.


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.


I think it's entirely a question of style. Neither really has an obvious advantage over the other.

Consistency is more important than either particular choice, so I'd recommend that you get together with your team and pick one style, and stick to it.


I myself prefer:

#if defined(DEBUG_ENABLED)

Since it makes it easier to create code that looks for the opposite condition much easier to spot:

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)