What is a C++ Preprocessor?
C++ Preprocessing means executing or processing the program file before the main program.
The preprocessor updates the source program file according to the preprocessor directives in C++ as part of the initial step while in the compiling process or translation of a C++ program.
Preprocessor Directives
Preprocessor directives in C++ are denoted by the #
symbol.
These directives give the preprocessor instructions on how to improve the program’s performance or extend its capabilities.
Preprocessor directives only apply to a single line of code; they end at the first newline character.
There is no semicolon at the end of a preprocessor directive.
If you want a directive to span multiple lines, you must end the line with a backslash character.
Each directive is a single-line command that includes the following:
- A
#
(hash) symbol (All C++ preprocessor directives begin with the#
(hash) character.
- A preprocessor instruction follows the
#
character. For example,#include, #define, #ifdef, #elif, #error, #pragma etc
.
- Arguments for directives are based on their type. For example, the
<iostream>
the directive uses the argumentPI 3.14
.
The #define Preprocessor C++
According to cppreference about Preprocessor Directives, the preprocessing directives control the behavior of the preprocessor.
Each directive occupies one line and #define defines macros in C++ preprocessor directives.
A macro is a constant value or named expression that can be used throughout a C++ program.
When a C++ program has a #define directive, the defined macros name substitutes it with a constant value or expression.
The # and ## Operators
The operators #
and ##
are considered to be preprocessor operators. An example of their usage in a C++ program is below:
- Stringizing Operator (#): This operator converts macro parameters to string literals without expanding the definition of the macro parameter. The stringizing operator turns the arguments passed into macros into quoted strings.
- Token-Pasting Operator (##): This operator allows tokens or arguments to be combined to create new variables. For instance, the two variables
(x,y)
can be collapsed into a single variable denoted asxy
.
Example:
#include<iostream>
#define stringer(str) #str
#define mix(x, y) x##y
using namespace std;
int main()
{
cout << stringer(argument_sample) << endl;
int xy = 50;
cout << mix(x, y);
return 0;
}
Using the stringizing operator (#)
, the stringer()
macro turns cout << stringer(argument_sample)
into cout << "argument_sample";
, which is a string.
The mix(x, y)
macro uses the Token-pasting operator (##)
to combine (x, y)
into one variable.
Output:
argument_sample
50
What are the Types of C++ Preprocessor?
Preprocessor directives come in four main types: Macros Directives, File Inclusion Directives, Conditional Compilation Directives, and Other Directives.
Macros Directives
Macros are pieces of code in a program that are given a name. When the compiler encounters "#define"
, it replaces it with the actual piece of code.
For example:
#include <iostream>
#define LIMIT 3
using namespace std;
int main()
{
for (int i = 0; i < LIMIT; i++) {
cout << i << endl;
}
return 0;
}
Output:
0
1
2
In the above example, the word "LIMIT"
in the macro definition is a placeholder for the value "3"
.
Note: macro definitions do not end with a semicolon - they don't need one.
Macros have four types, and they are: Object-like Macros, Chain Macros, Multi-line Macros, and Function-like Macro.
Predefined Macros
C++ has both predefined and user-defined macros.
Predefined macros are those already defined by the compiler, while the user in a C++ program defines user-defined macros.
Predefined macros can be used directly in a C++ program.
Predefined Macros | Definition |
---|---|
__cplusplus | It’s an integer literal value representing the C++ compiler version at which our source code file is compiled. |
__DATE__ | It’s a constant-length string literal in Mmm-dd-yyyy format replaced with the compile date. |
__TIME__ | It’s a hh:mm:ss character string literal that’s replaced by the compilation time. |
__FILE__ | It is also a character string literal which is replaced by the source code file path/name from where it is stored in the computer during the pre-processing. |
__LINE__ | This directive is replaced with the source code line number where the compiler encounters it during pre-processing. |
Example:
#include<iostream> using namespace std; int main() { cout << "C++ Version : "<<__cplusplus << endl; cout << "Date : "<<__DATE__ << endl; cout << "Time : "<<__TIME__ << endl; cout << "File Name : "<<__FILE__ << endl; cout << "Line Number : "<<__LINE__ << endl; return 0; }
Output:
C++ Compiler Version : 201402
Date : Aug 8 2022
Time : 16:36:26
File Name : cpp tutorials.cpp
Line Number : 11
The above C++ program prints the values of all the commonly used predefined macros.
Macros with Arguments
We can pass arguments to macros, which work similarly to functions.
For example:
#include <iostream>
#define AREA(x, b) (x * b)
using namespace std;
int main()
{
int x1 = 50, x2 = 20, form_area;
form_area = AREA(x1, x2);
cout << "The area of rectangle is: " << form_area;
return 0;
}
Output:
The area of rectangle is: 1000
The compiler will replace AREA(x, b)
with (x*b)
in the program.
This includes any values passed to AREA(x, b)
which will be replaced in (x*b)
.
The calculation for AREA(10, 5)
will be equivalent to 10 times 5.
File Inclusion Directives
With this type of preprocessor directive, the compiler is told to add a file to the source code program.
There are two types of files that a user can add to the program:
Header File or Standard Files
Pre-defined functions like printf()
and scanf()
are declared in header files.
Different functions are in different header files.
For example, standard I/O functions are in iostream, and functions that perform string operations are in a string.
Syntax:
#include<file_name>
Example:
#include<iostream>
int main{
std::cout << "C++ is FUN!";
}
Include a file by placing the file’s name between '<'
and '>'
brackets.
The compiler will look for the iostream
file in the standard directory.
Output:
C++ is FUN!
User Defined Files
It is good practice to divide a large program into smaller files, which can be included as needed. These files are user-defined.
Syntax:
#include"file_name"
Example:
To create a header file, write your code in a file and save it with an ".h"
extension (for example, myformula.h
).
For files that contain header information, the ".h"
an extension is utilized.
// inside the myformula.h file
int myformula(int x, int y, int z)
{
return((x+y+z)/3)
}
The above code comes from the myformula.h
header file.
Open a new file and write the code for finding the average of three numbers.
Include the header file "myformula.h"
using #include
and double-quotes. You should use the .cpp
extension when saving the file.
#include<iostream>
#include"myformula.h"
using namespace std;
int main()
{
int x = 5;
int y = 10;
int z = 15;
cout << "Average: " << myformula(x,y,z) << endl;
}
After saving the file, run the code to get the average of three numbers.
Output:
Average: 10
Conditional Compilation Directives
Conditional Compilation directives help to compile or skip specific parts of the program based on conditions.
This is done with "ifdef"
, "ifndef"
, "endif"
, "if"
, "else"
and "elif"
preprocessing commands.
Syntax:
#ifdef macro_name
statement1;
statement2;
statement3;
.
.
statementN;
#endif
If the "macro_name"
is defined, the compiler will execute the following code block.
In the event that it is not defined, the compiler will move on to the next item.
Example:
Printing age if macro is defined, else printing "there's no defined"
#include <iostream>
using namespace std;
#define AGE 25
// #define PI 3.14
int main()
{
#ifdef PI
printf("PI value: ", PI);
#elif AGE
printf("You're %d years old.", AGE);
#else
printf("there's no defined");
#endif
return 0;
}
The AGE
and PI
macros have been defined and commented.
In the main
function, if PI
is defined, its value will be printed; if AGE
is defined, its value will be printed; otherwise, "there's no defined"
will be printed.
Output:
You're 25 years old.
Other Directives
Two other directives are rarely used in addition to the directives described above.
#undef Directive
The #undef directive is used to undefine a macro that already exists.
This directive performs its functions as:
#undef AGE
If you use this statement, the macro AGE
will be undefined. This will cause every "#ifdef AGE"
statement to be evaluated to a false value.
Example: Using the #undef
, we can remove already defined macros.
#include<iostream>
#define AGE 125
int main()
{
printf("%d", AGE);
#undef AGE
printf("%d", AGE);
return 0;
}
The code above will prompt an error message that the ‘AGE’ was not declared in this scope.
The program runs correctly because we declared AGE
it as an integer variable after removing the previously defined macro instead of using the macro.
#include <iostream>
#define AGE 125
int main()
{
printf("%d", AGE);
#undef AGE
int LIMIT = 1001;
printf("\n%d", AGE);
return 0;
}
The other directives are #pragma Directive
, #pragma warn Directive
, #pragma warn -rvl
, #error
, and #line
.
Summary
In this lesson, C++ Program compilation begins with pre-processor commands.
Macro, file inclusion, conditional compilation, error, pragma, and others are C++ preprocessor directives.
And preprocessors can import program files into source code, expand macros, and conditionally compile code.
I hope you understand how to use preprocessor in C++ properly.
If you missed any of our previous lessons, check out our list of C++ Programming Tutorials for Beginners.