Conditional Compilation

Beyond macro substitution, a more important reason to use #define is to support conditional compilation. Using #define, and some other preprocessor directives, we can instruct the compiler to compile only certain sections of our source code. This is useful in many circumstances, one of which is for inserting debugging code that can be easily enabled and disabled. Below we see an example that uses the #define, #if, and #endif directives.

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: #include <iostream>#include <cstdlib> #define DEBUG using namespace std; int main(int argc, char* argv[]) { #if defined(DEBUG) cerr << "Debugging enabled" << endl;#endif int arr[10]; for (int i = 0; i < 10; i++) { arr[i] = i; #if defined(DEBUG) cerr << "i = " << i << endl; cerr << "arr[i] = " << arr[i] << endl;#endif } return EXIT_SUCCESS;}
Listing 4 Conditional compilation

The #if preprocessor directive works similar to a regular if-statement, except that it has to paired with an #endif directive. These two directives partition a section of source code that can be conditionally compiled. The preprocessor evaluates the value that follows the #if. If this value evaluates to true (non-zero), the preprocessor includes the source code block. If it evaluates to false, the preprocessor omits the source code block. In the above listing, defined(DEBUG) follows the #if directives. The preprocessor evaluates this to true only if we have defined an identifier named DEBUG. Since we have defined DEBUG in line 4, the source code blocks partitioned by the #if and #endif pairs will be compiled. The power of this technique is apparent when we realize all we have to do to disable the debugging code found throughout the program is remove the definition of DEBUG from the program. This causes the preprocessor to omit the debugging code.

Conditional compilation is also often used to prevent multiple definitions of classes and functions that are contained in header files. Including a header file more than once in a program can cause class and function redefinition problems. We can prevent this with a technique that uses conditional compilation. Below we see in line 1 the #if directive used to check if the program has defined the identifier _PERSON_H_. If it has not been defined, then the rest of the source code in the example is processed. If it has been defined, the source code is skipped by the preprocessor. The key to this technique is in line 2, where the program defines _PERSON_H_. If we had a program that had several source code files that all included the following header file, the conditional compilation would ensure that the content of the file is included only once. The first time the file was included would result in the definition of _PERSON_H_, which would then prevent the inclusion of the contents of the file a second time.

1: 2: 3: 4: 5: 6: 7: 8: 9: #if !defined(_PERSON_H_)#define _PERSON_H_ class Person { // Class declaration...}; #endif
Listing 5 Preventing multiple declarations

Shortcut conditional compilation constructs exist that we can use in place of the defined operator. The directive #ifdef identifier is equivalent to #if defined(identifier). Likewise, the directive #ifndef identifier is equivalent to #if !defined(identifier).