Препроцессоры - это директивы, которые дают инструкции компилятору для предварительной обработки информации до начала фактической компиляции.

Все директивы препроцессора начинаются с #, и перед директивой препроцессора на строке могут появляться только символы пробела. Директивы препроцессора не являются инструкциями C ++, поэтому они не заканчиваются точкой с запятой (;).

Вы уже видели директиву #include во всех примерах. Этот макрос используется для включения файла заголовка в исходный файл.

Существует ряд препроцессорных директив, поддерживаемых C ++ типа #include, #define, #if, #else, #line и т. д. Давайте посмотрим важные директивы -

Префикс #define

Директива префикса #define создает символические константы. Символьная константа называется макросом, а общая форма директивы:

#define macro-name replacement-text

Когда эта строка появляется в файле, все последующие вхождения макроса в этот файл будут заменены заменяющим текстом перед компиляцией программы. Например:

#include <iostream>
using namespace std;

#define PI 3.14159

int main () {
   cout << "Value of PI :" << PI << endl; 

   return 0;
}

Теперь давайте сделаем предварительную обработку этого кода, чтобы увидеть результат, предполагающий, что у нас есть файл исходного кода. Поэтому давайте скомпилируем его с опцией -E и перенаправим результат на test.p. Теперь, если вы проверите test.p, у него будет много информации, а внизу вы найдете значение, замененное следующим образом:

$gcc -E test.cpp > test.p

...
int main () {
   cout << "Value of PI :" << 3.14159 << endl; 
   return 0;
}

Функциональные макросы

Вы можете использовать #define для определения макроса, который будет принимать аргумент следующим образом:

#include <iostream>
using namespace std;

#define MIN(a,b) (((a)<(b)) ? a : b)

int main () {
   int i, j;
   
   i = 100;
   j = 30;
   
   cout <<"The minimum is " << MIN(i, j) << endl;

   return 0;
}

Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

The minimum is 30

Условная компиляция

Существует несколько директив, которые можно использовать для компиляции выборочных частей исходного кода вашей программы. Этот процесс называется условной компиляцией.

Конструкция условного препроцессора очень похожа на структуру выбора if. Рассмотрим следующий код препроцессора:

#ifndef NULL
   #define NULL 0
#endif

Вы можете скомпилировать программу для цели отладки. Вы также можете включить или отключить отладку с использованием одного макроса следующим образом:

#ifdef DEBUG
   cerr <<"Variable x = " << x << endl;
#endif

Это приводит к компиляции инструкции cerr в программе, если символьная константа DEBUG была определена до директивы #ifdef DEBUG. Вы можете использовать #if 0 statment, чтобы прокомментировать часть программы следующим образом:

#if 0
   code prevented from compiling
#endif

Попробуем следующий пример:

#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 commented part */
   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;
}

Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

The minimum is 30
Trace: Inside main function
Trace: Coming out of main function

Операторы # и ##

Операторы препроцессора # и ## доступны в C ++ и ANSI / ISO C. Оператор # заставляет токен заменяющего текста преобразовываться в строку, окруженную кавычками.

Рассмотрим следующее макроопределение -

#include <iostream>
using namespace std;

#define MKSTR( x ) #x

int main () {

   cout << MKSTR(HELLO C++) << endl;

   return 0;
}

Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

HELLO C++

Посмотрим, как это работает. Легко понять, что препроцессор C ++ превращает строку:

cout << MKSTR(HELLO C++) << endl;

Над строкой будет переведена следующая строка:

cout << "HELLO C++" << endl;

Оператор ## используется для объединения двух токенов.

#define CONCAT( x, y )  x ## y

Когда CONCAT появляется в программе, его аргументы конкатенируются и используются для замены макроса. Например, CONCAT (HELLO, C ++) заменяется на HELLO C ++ в программе следующим образом.

#include <iostream>
using namespace std;

#define concat(a, b) a ## b
int main() {
   int xy = 100;
   
   cout << concat(x, y);
   return 0;
}

Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

100

Посмотрим, как это работает. Легко понять, что препроцессор C ++ преобразует:

cout << concat(x, y);

Над строкой будет преобразована следующая строка:

cout << xy;

Предопределенные макросы C ++

C ++ предоставляет ряд предопределенных макросов, упомянутых ниже -

__LINE__

Он содержит текущий номер строки программы при компиляции.

__FILE__

Он содержит текущее имя файла программы при компиляции.

__DATE__

Он содержит строку формы month / day / year, которая является датой перевода исходного файла в объектный код.

__TIME__

Это содержит строку формы hour: minute: second - время, в которое была скомпилирована программа.

Давайте посмотрим пример для всех вышеперечисленных макросов:

#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;
}

Если мы скомпилируем и запустим код выше, это приведет к следующему результату:

Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48