Conditional compilation

As explained in Chapter 1, Starting with C++, when your C++ program is compiled there is a pre-compilation step that collates all the file included in a C++ source file into a single file, which is then compiled. The pre-processor also expands macros and, depending on the value of symbols, includes some code and exclude others code.

In its simplest form, conditional compilation brackets code with #ifdef and #endif (and optionally using #else), so that the code between these directives is only compiled if the specified symbol has been defined.

    #ifdef TEST 
cout << "TEST defined" << endl;
#else
cout << "TEST not defined" << endl;
#endif

You are guaranteed that only one of these lines will be compiled, and you are guaranteed that at least one of them will be compiled. If the symbol TEST is defined then the first line will be compiled and, as far as the compiler is concerned, the second line does not exist. If the symbol TEST is not defined, then the second line will be compiled. If you want to type these lines in the opposite order, you can use the #ifndef directive. The text provided through the conditional compilation can be C++ code, or it can be defined using other symbols in the current translation unit with #define or undefined existing symbols with #undef.

The #ifdef directive simply determines if the symbol exists: it does not test its value. The #if directive allows you to test an expression. You can set a symbol to have a value and compile specific code depending on the value. The expression must be integral, so a single #if block can test for several values using #if and multiple #elif directives and (at most) one #else:

    #if TEST < 0 
cout << "negative" << endl;
#elif TEST > 0
cout << "positive" << endl;
#else
cout << "zero or undefined" << endl;
#endif

If the symbol is not defined then the #if directive treats the symbol as having a value of 0; if you want to distinguish between these cases you can use the defined operator to test if a symbol is defined. At most, only one of the sections in the #if/#endif block will be compiled, and if a value is not matched then no code will be compiled. The expression can be a macro, in which case the macro will be expanded before the condition is tested.

There are three ways to define a symbol. The first way is out of your control: the compiler will define some symbols (typically with the __ or _ prefix) that give you information about the compiler and the compilation process. Some of these symbols will be described in a later section. The other two ways are entirely under your control--you can define symbols in a source file (or header file) using #define or you can define them on the command line using the /D switch:

    cl /EHsc prog.cpp /DTEST=1

This will compile the source code with the symbol TEST set to a value of 1.

You will typically use conditional compilation to provide code that should not be used in production code, for example, extra tracing code to use in debug mode or when you are testing code. For example, imagine you have library code to return data from a database, but you suspect that the SQL statement in the library function is faulty and returning too many values. Here, you may decide to test, add code to log the number of values returned:

    vector<int> data = get_data(); 
#if TRACE_LEVEL > 0
cout << "number of data items returned: " << data.size() << endl;
#endif

Trace messages like this pollute your user interface and you will want to avoid them in production code. However, in debugging they can be invaluable in determining where problems are occurring.

Any code that you call in debug mode, conditional code should be const methods (here vector::size), that is, they should not affect the state of any objects or the application data. You must ensure that the logic of your code is exactly the same in debug mode as in release mode.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset