Preprocessor directives

Location
  1. Courses

    /

  2. Complete C++ Course

    /

  3. The basics

    /

  4. Preprocessor directives

What are preprocessor directives?

Lines that start with a # are preprocessor directives. They are not instructions that will be executed in the program nor compiled by the compiler. In the compilation process, the preprocessor will read them, execute the directive and take them off before the compiler starts compiling the file.

Defining a macro

A macro is basically an expression that will be replaced (Where it appears in the code) by its value when the preprocessor is executed. We define a macro by writing #define followed by the name of the macro and then its value.

Example:

#define arraySize 10 int array[arraySize];

After the preprocessor is executed, the code above would become:

int array[10];

The preprocessor is not aware of how C/C++ works. All it does is mindlessly replace the macros by their value.

Here is an example of a more complex macro:

#define sum(a, b) (a + b) std::cout << sum(4, 9) << std::endl;

After the preprocessor ran, the example above would become:

std::cout << (4 + 9) < std::endl;

The keyword NULL, which represents an invalid memory address, is actually a macro. Its real value depends on the compiler used, but it may equal, for example, 0 or nullptr.

Undefining a macro

It is possible to undefine a macro by using the preprocessor directive undef. Example:

1
2
3
4
5
6
7
#define mult(a, b) (a * b) int a, b=2, c=6; a = mult(b, c); #undef mult(a, b) a = mult(a, b) // Error: The macro “mult(a, b)” does not exist anymore.

Conditionally include/exclude code

Preprocessor directives can be used to include portions of code only under specific circumstances. The ifdef directive will include the code above it until the directive endif is reached, only if the macro following it exists. Example:

#ifdef mult const int NUM = mult(6, 7); // This line is not included if the macro 'mult' does not exist. #endif

The directive ifndef does the same, except that it include the code only if the macro has not been defined. Example:

#ifndef mult // If the macro 'mult' does not exist. void mult(int a, int b) // Then, instead, we define a function with the name 'mult'. { return a * b; } #endif

There are also the directives if, else and elif that are similar to the C/C++ control structure if, else and else if. With them, we can only test macros and constant expressions (Numbers and text). Remember, the preprocessor does not understand C/C++. Our C/C++ variables does not exist from the preprocessor perspective.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define VERSION 1 #if VERSION == 1 void func() { std::cout << “I am the first version of the function ‘func’” << std::endl; } #elif VERSION == 2 void func() { std::cout << “I am the second version of the function ‘func’” << std::endl; } #else void func() { std::cout << “I am the default version of the function ‘func’” << std::endl; } #endif

In the example above, the operator == is used. However, we could have used any of the following operators: (>, >=, <, <=, !=, ==, !). It is also possible to use two special operators: defined and !defined. Using them transforms an if directive into an ifdef/ifndef directive. Example:

#if defined VERSION // If the macro 'VERSION' is defined #undef VERSION #elif !defined DEBUG // Else if the macro 'DEBUG' is not defined #define DEBUG #endif

Error directive

If an error directive is reached, the effect is a compilation error with the error message following the directive. Example:

#ifndef __cplusplus #error This file requires a C++ compiler to be compiled. #endif

The macro __cplusplus__ is automatically defined by all C++ compilers. The effect of the code above is that if the file is not being compiled by a C++ compiler (by a C compiler), then it will cause an error and warn the user.

Macro on multiple lines

We can write a macro on multiple lines by ending each line (That is not the last) with a backslash character.

#error An error message \ written on \ multiple lines. #define mult(a, b) \ (a * b)

Including files

The include directive is used to include the content of an other file into the current file. This is, by far, the most used preprocessor directive. After the preprocessor is executed, the directive will be replaced by the content of the file to include. In order to include a file that is part of the C or the C++ standard library, we must put the name of the file between <>. If the file is in the include folder of the compiler we are using to compile the project, then we must also write the name of the file between <>. For all other files, we must put its name between quotes. Example:

#include <iostream> // iostream is part of the C++ standard library. #include <stdlib.h> // stdlib.h is part of the C standard library. #include “myOtherFile.h” // This file is part of our project. #include “folder/myOtherFile.h” // This file is also part of our project.

Note that the files from the C standard library ends with “.h” but those of C++ does not. It is also possible to add the character c at the beginning of the name of the file from the C standard library instead of adding the ".h" at the end of it. Example:

#include <cstdlib>

Instead of:

#include <stdlib.h>

Pragma

The pragma directive is used to specify options to the compiler. However, those are specific to each compiler, so it is better to avoid them.

Some predefined macros

MacroValue
__LINE__The current number of the line in the code file.
__FILE__The name of the current code file.
__DATE__The date, when the compilation process started, in the format Mmm dd yyyy.
__TIME__The time, when the compilation process started, in the format hh:mm:ss.
__cplusplusIts value depends on the version of the C++ standard supported by the compiler.

Byte

We saw earlier that a byte is the smallest unit of memory a computer can natively handle and that it was not guaranteed to be an octet (made of 8 bits). The macro CHAR_BIT defined in the header file <limits.h> of the C standard library has as value the byte size of the machine the code is compiled on.