Препроцессорные директивы

Директива #include

#include </*имя заголовочного файла*/>

Директива #include вставляет код из указанного файла в текущий файл, то есть, просто подключив другой файл, мы можем пользоваться его функциями, классами, переменными. Заголовочные файлы обычно находятся либо в текущей директории, либо в стандартном системном каталоге.

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

Если подключаемый файл не найден, процесс компиляции завершается с ошибкой.

Директива #define

Директива #define принимает две формы:

  • определение констант;
  • определение макросов.
Определение констант
#define nameToken value

При использовании имени константы — nameToken, оно будет заменено значением value, то есть, грубо говоря — это та же самая переменная, значение которой изменить нельзя. Смотрим пример использования константы:

#include <iostream>

#define TEXT "Марс" // определение константы

int main()
{
    std::cout <<  TEXT;
    return 0;
}

Как видите, для доступа к значению константы, просто используем её имя.

Определение параметризованных макросов

#define nameMacros(arg1, arg2, ... ) expression

К примеру определим макрос, который будет возвращать максимальное из двух значений.

#define MAX(num1, num2) ((num1) > (num2) ? (num1) : (num2))

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

Директива #undef

Директива #undef переопределяет константу или препроцессорный макрос, ранее определенный с помощью директивы #define.

#undef nameToken

Давайте посмотрим пример использования директивы #undef:

#define E 2.71828 // раннее определенный макрос
int sumE = E + E; // обращение к макросу
#undef E // теперь E - не макрос

Как правило, директива #undef используются для снятия, ранее определенной константы или макроса, в небольшой области программы. Это делается для того, чтобы для всей программы, макроc или константа оставались, а для некоторой области, эти же макрос или константа могут быть переопределены. Небезопасно было бы во всей программе переопределять константу, но в короткий области, это сравнительно безопасно. Директива #undef является единственным способом создания этой области, так как область действия макросов или констант действует от директивы #define до #undef.

Директива #if

#if value
// код, который выполнится, в случае, если value - истина
#elsif value1
// этот код выполнится, в случае, если value1 - истина
#else
// код, который выполнится в противном случае
#endif

Директива #if проверяет, является ли значение value истиной и, если это так, то выполняется код, который стоит до закрывающей директивы #endif. В противном случае, код внутри #if не будет компилироваться, он будет удален компилятором, но это не влияет на исходный код в исходнике.

Обратите внимание, что в #if могут быть вложенные директивы #elsif и #else. Ниже показан пример кода для комментирования блоков кода, используя следующую конструкцию:

#if 0
// код, который необходимо закомментировать
#endif

Если у вас в программе есть блоки кода, которые содержат многострочные комментарии и вам требуется обернуть полностью этот блок кода в комментарий — ничего не получится, если вы воспользуетесь /*многострочный комментарий*/.  Другое дело — конструкция директив #if #endif.

Директива #ifdef

#ifdef nameToken
// код, который выполнится, если nameToken определен
#else
// код, который выполнится, если nameToken не определен
#endif

Директива #ifdef проверяет, был ли ранее определен макрос или символическая константа как #define. Если — да, компилятор включает в программу код, который находится между директивами #ifdef и #else, если nameToken ранее определен не был, то выполняется код между #else и #endif, или, если нет директивы #else, компилятор сразу переходит к #endif. Например, макрос __cpp определен в C++, но не в Си. Вы можете использовать этот факт для смешивания C и C++ кода, используя директиву #ifdef:

#ifdef __cpp
// C++ код
#else
// Си код
#endif

Директива #ifndef

#ifndef nameToken
// код, который выполнится, если nameToken не определен
#else
// код, который выполнится, если nameToken определен
#endif

Директива #ifndef проверяет, был ли ранее определен макрос или символическая константа как #define. Если — да, компилятор включает в программу код, который находится между директивами  #else и #endif, если nameToken ранее определен не был, то выполняется код между #ifndef и #else, или, если нет директивы #else, компилятор сразу переходит к #endif.  Директива #ifndef может быть использована для подключения заголовочных файлов. если они не подключены, для этого использовать символическую константу, как индикатор подключенного к проекту функционала.

Например, в заголовочном файле есть интерфейс класса, который необходимо подключить к проекту, если ранее этот класс не был подключен.

#ifndef PRODUCT_H
#define PRODUCT_H

class Product
{
// код класса...
};

#endif  PRODUCT_H

В этом случае используется пустая символьная константа PRODUCT_H, которая может быть определена в программе только вместе с классом Product. Поэтому, если мы обнаружим, что константа PRODUCT_H уже определена, значит класс тоже и тогда мы исключим повторное определение класса, которое может привести к ошибке переопределения.

 Директива #error

#error "Этот код не должен компилироваться"

Директива #error позволяет отображать в списке ошибок компиляции сообщение, в случае возникновения соответствующей ошибки. Эту директиву наиболее полезно использовать в сочетании с директивами #if#elsif, #else для проверки компиляции, если некоторое условие не верно. Например:

#ifndef __unix__ // __unix__ обычно поддерживается в юникс-системах 
#error "Поддерживается только в Unix"
#endif

Препроцессорный макрос __FILE__

Препроцессорный макрос __FILE__ расширяется до полного пути к текущему файлу (исходнику). __FILE__  полезен при создании лог-файла, генерации сообщений об ошибках, предназначенных для программистов, при отладки кода.

int error (const char* adrFile, const std::string& erMessage)
{
    cerr << "[" << adrFile << "]" << arMessage << endl;
}
#define LOG( erMessage ) error( __FILE__, arMessage )

// макрос LOG может быть использован для получения сообщений об ошибках, которые выводятся на стандартный поток ошибок

Макрос __FILE__ часто используется совместно с макросом __LINE__, который предоставляет номер текущей строки.

Препроцессорный макрос __LINE__

Макрос __LINE__ разворачивается в текущий номер строки в исходном файле, как целое значение. __LINE__  полезен при создании лог-файла или генерации сообщений об ошибках с указанием номера строки,  предназначенных для программистов, при отладки кода.

int error (int nLine, const std::string& erMessage)
{
    cerr << "[" << nLine << "]" << erMessage << endl;
}
#define LOG( erMessage ) error( __LINE__, erMessage )

// макрос LOG может быть использован для получения сообщений об ошибках, с указанием номеров строк, которые выводятся на стандартный поток ошибок

Макрос __LINE__ часто используется совместно с макросом __FILE__, который показывает адрес текущего исходного файла.

Препроцессорный макрос __DATE__

Макрос __DATE__ раскрывается в текущую дату (время компиляции) в виде [ммм дд гггг] (например, «Dec 7 2012″), как строка. __DATE__ может быть использован для предоставления информации о времени компиляции.

 cout << __DATE__ << endl;

Вы можете также использовать макрос __TIME__, чтобы получить текущее время компиляции.

Препроцессорный макрос __TIME__

Макрос __TIME__ раскрывается в текущее время (время компиляции) в формате чч: мм:cc в 24-часовом формате (например, «22:29:12″). Макрос __TIME__ может быть использован для предоставления информации о времени в конкретный момент компиляции.

cout << __TIME__ << endl;

Препроцессорный макрос __TIMESTAMP__

Макрос __TIMESTAMP__ раскрывается в текущее время (время компиляции) в формате Ddd Mmm Date hh::mm::ss yyyy, время в 24-часовом формате:

  • Ddd это сокращенно день недели,
  • ммм это сокращенно месяц ,
  • Date — текущий день месяца (1-31),
  • гггг — это четыре цифры года.

Например, "Fri Dec  7 00:42:53 2012". Макрос __TIMESTAMP__ может быть использован для получения информации о дате и времени компиляции.

cout << __TIMESTAMP__ << endl;

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

Директива #pragma

#pragma compiler specific extension

Директива #pragma используется для доступа к специфическим расширениям компилятора. Совместное использование директивы #pragma c лексемой once просит компилятор включить файл заголовка только один раз, независимо от того, сколько раз она был импортирован:

#pragma once
// заголовочный файл

В этом примере, директива #pragma once не позволяет включать файл в проект несколько раз, то есть предотвращает переопределение.

Директива #pragma также может быть использована для других целей, например #pragma обычно используется для отключения предупреждений. Например, в MVS:

#pragma warning (disable : 4018 )

Директива #pragma в этом примере используется для отключения предупреждений 4018. Для получения дополнительной использования директивы #pragma, обратитесь к документации вашего компилятора.

Макро оператор #

#<token>

Оператор # текстовую лексему в строку, заключенную в кавычку. Смотрим пример:

#include <iostream>
using namespace std;

#define message(s) cout << "Сообщение: " #s << endl;

int main() {

    message("GunGame");

return 0;
}

Выполняется конкатенация строк и макрос message разворачивается в cout << "Сообщение: GunGamen";.  Обратите внимание на то, что операция # должна использоваться совместно с аргументами, так как # ссылается на аргумент.

Макро оператор ##

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

#define type ch##ar
type a; // переменная a тип данных char, так как ch и ar склеились в char

Рассмотрим еще один пример использования оператора ##, в котором объединим две лексемы:

#define TOKENCONCAT(x,y) x##y

Когда в программе выполняется вызов этого макроса, две лексемы объединяются в одну. Операция ## обязательно должна иметь два операнда.

P.S.: Любая серьёзная программа должна иметь свою базу данных, обычно для управления БД используются следующие СУБД: MySQL, MsSQL, PostgreeSQL, Oracle и др. В установке sql server, форум на cyberforum.ru будет для вас не заменимым помощником. Задавайте свои вопросы на этом форуме, вам обязательно помогут в решении вашей проблемы.

Автор: Marienko L.
Дата: 07.12.2012
Поделиться:

Комментарии

  1. yurasik90

    yurasik90

    #elsif не везде работает, можно заменить на #elif

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.