News & UpdatesProgrammingWeb programming StoreMy Projects
Links
Affiliates

C++ Tutorial – 25 – Preprocessor

The preprocessor is a program invoked by the compiler that modifies the source code before the actual compilation takes place. This modification is done according to the preprocessor directives that are included in the source files. The directives are easily distinguished from normal programming code in that they all start with a hash sign (#). They must always appear as the first non-whitespace character on a line. Below is a table of the preprocessor directives available in C++ and their function.

DirectiveDescription
#includeFile include
#define
#undef
Macro definition
Macro undefine
#ifdef
#ifndef
If macro defined
If macro not defined
#if
#elif
#else
#endif
If
Else if
Else
End if
#line
#error
#pragma
Set line number
Abort compilation
Set compiler option

Including source files

The #include directive inserts the contents of a file into the current source file. If the filename is enclosed between angle brackets the compiler will search for the file in the default directory where it is configured to look for the standard header files.

#include <iostream> // search default directory

If instead the filename is specified between double-quotes, the compiler will first search for the file in the same directory as the source file and in case it is not there it will then search among the standard header files.

#include "MyFile" // search current, then default

Double quotes can also be used to specify an absolute or relative path to the file.

#include "C:\MyFile" // absolute path
#include "..\MyFile" // relative path

The quoted form of #include is normally used for programmer defined headers, while the angle bracket form is used for standard library headers.

Defining macros

Another important directive is #define, which is used to create macros. After the directive, the name of the macro is specified followed by what it will be replaced by.

#define MACRO 0 // macro definition

The preprocessor will go through and change any occurrences of the macro with whatever comes after it in its definition until the end of the line.

int x = MACRO; // x = 0

By convention, macros should be named in uppercase letters with each word separated by an underscore. That way they are easy to spot when reading the source code.

Undefining macros

A #define directive should not be used to directly override a previously defined macro. Doing so will give a compiler warning. In order to change a macro it first needs to be undefined using the #undef directive before it is redefined. Note that attempting to undefine a macro that is not currently defined will not generate a warning.

#undef MACRO // macro undefine
#undef MACRO // allowed

Macro functions

A macro can be made to take arguments. This allows them to define compile-time functions. For example, the macro function below evaluates to the largest of its two arguments.

#define MAX(a,b) a>b ? a:b

The macro function is called just as if it was a regular C++ function. Keep in mind that for this kind of function to work the arguments must be known at compile-time.

int x = MAX(MACRO, 1); // evaluates to 1

Line-break

To break a macro function across several lines the backslash character can be used. This will escape the newline character that marks the end of a preprocessor directive. There must not be any whitespace after the backslash.

#define MAX(a,b)  \
            a>b ? \
            a:b

Avoid using macros

Although #define directives can be powerful, they tend to make the code more difficult to read and debug. Macros should therefore only be used when they are absolutely necessary. C++ code such as constant variables, enums and inline functions can often accomplish the same goal more efficiently and safely than #define directives can.

#define DEBUG 0
const bool DEBUG = 0;
 
#define FORWARD 1
#define STOP 0
#define BACKWARD -1
enum DIR { FORWARD = 1, STOP = 0, BACKWARD = -1 };
 
#define MAX(a,b) a>b ? a:b
inline int MAX(int a, int b) { return a>b ? a:b; }

Conditional compilation directives

The directives used for conditional compilation can include or exclude part of the source code if a certain condition is met. First, there is the #if and #endif directives, which specifies a section of code that will only be included if the condition after the #if directive is true. Note that this condition must evaluate to a constant expression.

#define DEBUG_LEVEL 3
 
#if DEBUG_LEVEL > 2
 // …
#endif

Just as with the C++ if statement, any number of #elif (else if) directives and one final #else directive can be included.

#if DEBUG_LEVEL > 2
 // …
#elif DEBUG_LEVEL == 2
 // …
#else
 // …
#endif

Compile if defined

Sometimes, a section of code should only be compiled if a certain macro has been defined, irrespective of its value. For this purpose two special operators can be used: defined and !defined (not defined).

#define DEBUG
 
#if defined DEBUG
 // …
#elif !defined DEBUG
 // …
#endif

The same effect can also be achieved using the directives #ifdef and #ifndef respectively. For instance, the #ifdef section is only compiled if the specified macro has been previously defined. Note that a macro is considered defined even if it has not been given a value.

#ifdef DEBUG
 // …
#endif
 
#ifndef DEBUG
 // …
#endif

Error directive

When the #error directive is encountered the compilation is aborted. This directive can be useful for example to determine whether or not a certain line of code is being compiled. It can optionally take a parameter that specifies the description of the generated compilation error.

#error Compilation aborted

Line directive

Another standard directive is #line, which can be used to change the line number that is displayed when an error occurs during compilation. Following this directive the line number will as usual be increased by one for each successive line. The directive can take an optional string parameter that sets the filename that will be shown when an error occurs.

#line 5 "My MyApp Error"

Pragma directive

The last standard directive is #pragma, or pragmatic information. This directive is used to specify options to the compiler. For example, #pragma message can be used to have the compiler output a string to the build window. Another argument for this directive is warning, which changes how the compiler handles warnings.

// Show compiler message
#pragma message( "Hello Compiler" )
 
// Disable warning 4507
#pragma warning(disable : 4507)
Recommended additional reading:
Sams - Teach Yourself C++ in One Hour a Day