C++ Preprocessor
The preprocessor is a set of directives that instruct the compiler to perform necessary preprocessing before actual compilation.
All preprocessor directives start with a hash symbol (#), and only space characters can appear before a preprocessor directive. Preprocessor directives are not C++ statements, so they do not end with a semicolon (;).
We have seen that all previous examples include the #include directive. This macro is used to include header files in the source file.
C++ supports many preprocessor directives, such as #include, #define, #if, #else, #line, etc. Let's take a look at these important directives.
#define Preprocessing
The #define preprocessor directive is used to create symbolic constants. This symbolic constant is commonly referred to as a macro, and the general form of the directive is:
#define macro-name replacement-text
When this line of code appears in a file, all subsequent occurrences of the macro in the file will be replaced with the replacement-text before the program is compiled. For example:
#include <iostream>
using namespace std;
#define PI 3.14159
int main ()
{
cout << "Value of PI :" << PI << endl;
return 0;
}
Now, let's test this code to see the result of preprocessing. Assuming the source code file already exists, compile it with the -E option and redirect the output to test.p. If you look at the test.p file, you will see it contains a lot of information, and the value at the bottom is changed to:
$ gcc -E test.cpp > test.p
...
int main ()
{
cout << "Value of PI :" << 3.14159 << endl;
return 0;
}
Parameterized Macros
You can use #define to define a macro with parameters, as shown below:
#include <iostream>
using namespace std;
#define MIN(a,b) (a<b ? a : b)
int main ()
{
int i, j;
i = 100;
j = 30;
cout << "The smaller value is: " << MIN(i, j) << endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
The smaller value is: 30
Conditional Compilation
Several directives can be used to selectively compile parts of the program source code. This process is called conditional compilation.
The structure of the conditional preprocessor is similar to the if selection structure. Consider the following preprocessor code:
#ifdef NULL
#define NULL 0
#endif
You can compile only for debugging purposes, using a macro for the debug switch, as shown below:
#ifdef DEBUG
cerr << "Variable x = " << x << endl;
#endif
If the symbolic constant DEBUG is defined before the directive #ifdef DEBUG, the cerr statement in the program will be compiled. You can use #if 0 to comment out a part of the program, as shown below:
#if 0
Code not to be compiled
#endif
Let's try the following example:
Example
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main ()
{
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr << "Trace: Inside main function" << endl;
#endif
#if 0
/* This is a comment section */
cout << MKSTR(HELLO C++) << endl;
#endif
cout << "The minimum is " << MIN(i, j) << endl;
#ifdef DEBUG
cerr << "Trace: Coming out of main function" << endl;
#endif
return 0;
}
When the above code is compiled and executed, it produces the following result:
Trace: Inside main function
The minimum is 30
Trace: Coming out of main function
# and ## Operators
The # and ## preprocessor operators are available in C++ and ANSI/ISO C. The # operator converts the replacement-text token into a string enclosed in quotes.
Consider the following macro definition:
Example
#include <iostream>
using namespace std;
#define MKSTR(x) #x
int main ()
{
cout << MKSTR(HELLO C++) << endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
HELLO C++
The ## operator is used to concatenate two tokens. Here is an example:
#define CONCAT(x, y) x ## y
int main()
{
int xy = 100;
cout << CONCAT(x, y);
return 0;
}
When the above code is compiled and executed, it produces the following result:
100
using namespace std;
#define MKSTR(x) #x
int main()
{
cout << MKSTR(HELLO C++) << endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
HELLO C++
Let's see how it works. It's easy to understand that the C++ preprocessor converts the following line:
cout << MKSTR(HELLO C++) << endl;
into:
cout << "HELLO C++" << endl;
Operator is used to concatenate two tokens. Here is an example:
#define CONCAT(x, y) x ## y
When CONCAT appears in the program, its arguments are concatenated and used to replace the macro. For example, CONCAT(HELLO, C++) in the program will be replaced with "HELLO C++", as shown in the following example.
Example
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main()
{
int xy = 100;
cout << concat(x, y);
return 0;
}
When the above code is compiled and executed, it produces the following result:
100
Let's see how it works. It's easy to understand that the C++ preprocessor converts the following line:
cout << concat(x, y);
into:
cout << xy;
Predefined Macros in C++
C++ provides some predefined macros as shown in the following table:
Macro | Description |
---|---|
__LINE__ | This contains the current line number when the program is compiled. |
__FILE__ | This contains the current file name when the program is compiled. |
__DATE__ | This contains a string in the form of month/day/year, representing the date when the source file was converted to the target code. |
__TIME__ | This contains a string in the form of hour:minute:second, representing the time when the program was compiled. |
Let's see an example of these macros:
Example
#include <iostream>
using namespace std;
int main()
{
cout << "Value of __LINE__ : " << __LINE__ << endl;
cout << "Value of __FILE__ : " << __FILE__ << endl;
cout << "Value of __DATE__ : " << __DATE__ << endl;
cout << "Value of __TIME__ : " << __TIME__ << endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48