The Preprocessor
The preprocessor runs before the compiler examining the code for preprocessing directives. Preprocessor directives operate outside of the scope of the application instructing the compiler to perform certain actions at compile time, such as to ignore certain platform-specific code or include other source files. The result of preprocessing is a single file which is then passed to the compiler. Each directive occupies one line and is characterised by the fact that they all start with a # sign.
The C++ preprocessor contains the following directives: #define, #error, #include, #if, #else, #elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma
#define
#define is used to allow constant values to be declared for use throughout your code that will be substituted for the identifier each time it is encountered in the source file.-
#define MVALUE 1
#define WEBNAME "webaddress.com"
Macros Function when the macro name is encountered, the arguments associated with it are replaced by the actual arguments found in the program-
#include <iostream> using namespace std; #define MAX(x,y)(((x)>(y)) ? x : y) int main() { int v1, v2; v1 = 5; v2 = 15; cout << "The maximum value is " << MAX(v1, v2); return 0; }
When this above program is compiled, the expression MAX(a,b) will be substituted, except that v1 and v2 will be used as the operands.
#include
#include instructs the compiler to include either a standard header or another source file. The name of the standard headers is enclosed between angled brackets such as <iostream>. When including another source file, the name of the source file is enclosed between double quotes such as "sapi.h".
If the filename is enclosed between angle brackets, the preprocessor searches for it in one or more implementation-defined directories pre-designated by the compiler/IDE. If the filename is enclosed between quotes, then the compiler typically searches for it in the same directory as the file containing the directive. If the file is not found in this directory, the search is restarted as if the filename had been enclosed between angle brackets. Since the search path is implementation-defined, it will be necessary to check the compiler’s documentation to check for further details.
Conditional Compilation Directives
These directives allow the selective compilation of a program’s source code. The conditional directives are #if, #else, #elif, #endif, #ifdef and #ifndef.
The #if directive evaluates an expression and then compiles the code based on the evaluation outcome #endif is used to mark the end of an #if block. The #else and #elif (else if) are is used to test for multiple compilation options.
#include <iostream> using namespace std; #define diskspace 40 int main() { #if diskspace<=50 cout << "disk space is lower that 50.\n"; #elif diskspace<=100 cout << "disk space is between 50 and 100.\n"; #elif diskspace<=150 cout << "disk space is between 100 and 150.\n"; #else diskspace>150 cout << "disk space>150\n"; #endif // ... return 0; }
#ifdef and #ifndef
refers to whether a macro is defined or not defined-
#include <iostream> using namespace std; #define PAUL int main() { #ifdef PAUL cout << "Paul is defined.\n"; #endif #ifndef PAUL cout << "Paul is not defined.\n"; #endif #ifdef SIMON cout << "Simon is defined.\n"; #endif #ifndef SIMON cout << "Simon is not defined.\n"; #endif return 0; }
defined directive-the #if directive used in conjunction with the defined operator can also be used to determine whether a macro name is defined. in the above code, replacing #ifdef with #if defined PAUL would also work.
#undef
The #undef directive is used to remove a previously defined definition of a macro name allowing compartmentalisation of code-
#define VALUE 100
#undef VALUE
#error
The #error directive terminates compilation and outputs the text following the directive. This directive is used primarily for debugging. The declaration syntax is -
#error error-message
Double quotes are not used to display the error message. When the compiler encounters this directive, the error message is displayed and then compilation is terminated.
#line
The #line command is used to change the value of the __LINE__ and the __FILE__ variable. The variables represent the current line numbers and file respectively
#line 100 "main.cpp"
The above command changes the current line number to 100, and the current file to "main.cpp".
cout << __LINE__ would print the value 100 and cout << __FILE__ would display the value main.cpp
#pragma
The #pragma directive is a compiler-specific directive that allows various compiler-specific instructions to be given to the compiler.
The # and ## Preprocessor Operators
The # operator causes a replacement-text token to be converted to a string surrounded by quotes-
#include <iostream> using namespace std; #define MAKESTRING( x ) #x int main () { cout << MAKESTRING(HELLO WORLD) << endl; return 0; }
The ## operator is used to concatenate two tokens.
#include <iostream> using namespace std; #define conbine(x, y) x ## y int main() { int ab = 10; cout << conbine(a,b); return 0; }
The preprocessor transforms cout << conbine(a, b) into cout << ab;
Predefined Macro Names
C++ specifies a range of predefined macro names. Some of the macros come as standard, others are compiled or system-specific.-
__LINE__ contains the current line number of the program when it is being compiled.
__FILE__ contains the current file name of the program when it is being compiled.
__DATE__ contains the date of the translation of the source file into object code. This data is represented by a string in the format month/day/year.
__TIME__ contains the time at which the program was compiled. The time is represented in a string in the format hour:minute:second
__STDC__n if defined to 1, the implementation conforms to standard C/C++ code and does not contain any non-standard extensions.
__cplusplus will be defined as a value containing at least 6 digits is the compiler conforms to ANSI/ISO Standard C++. Non-conforming compilers will use a value with 5 or fewer digits.
When checking the value of non-standard predefined macros checked that the macro exists before checking its value-
#include <iostream> using namespace std; int main() { #ifdef __linux__ //code specific to linux platform cout << "value __linux__ is "; cout << __linux__<< endl; #endif # if defined(_WIN32) //code specific to windows 32 bit platform cout << "value _WIN32 is "; cout << _WIN32 << endl; #endif #if defined(_MSC_VER) //code specific to MS Visual Studio cout << "value _MSC_VER is "; cout << _MSC_VER << endl; #endif #if defined(__MINGW64__) //code specific to MS Visual Studio cout << "value __MINGW64__ is "; cout << __MINGW64__<< endl; #endif #if defined(__GNUC__) //code specific to GNUC compiler cout << "value __GNUC__ is "; cout << __GNUC__<< endl; #endif #if defined(_WIN64) //code specific to windows 64 bit platform cout << "value _WIN64 is "; cout << _WIN32 << endl; #endif return 0; }