Spec-Zone.ru › C++

Замена текстовых макросов

Предпроцессор поддерживает замену текстовых макросов. Также поддерживается замена макросов с поведением, подобным функциям.

Синтаксис

#define идентификатор список-замены (необязательно) (1)
#define идентификатор (параметры ) список-замены (необязательно) (2)
#define идентификатор (параметры , ...) список-замены (необязательно) (3) (с C++11)
#define идентификатор (...) список-замены (необязательно) (4) (с C++11)
#undef идентификатор (5)

Объяснение

Директивы #define

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

Макросы-объекты

Макросы-объекты заменяют каждое вхождение определенного идентификатора на список-замены. Версия (1) директивы #define ведет себя точно так же.

Макросы-функции

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

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

Для версии (2) количество аргументов должно совпадать с количеством параметров в определении макроса. Для версий (3,4) количество аргументов не должно быть меньше количества параметров (не(с C++20) считая ...). В противном случае программа считается ошибочной. Если идентификатор не имеет функциональной нотации, то есть после него нет скобок, он не заменяется вообще.

Версия (2) директивы #define определяет простой макрос-функцию.

Версия (3) директивы #define определяет макрос-функцию с переменным количеством аргументов. Дополнительные аргументы (называемые переменными аргументами) можно получить, используя __VA_ARGS__ идентификатор, который затем заменяется аргументами, предоставленными с идентификатором, подлежащим замене.

Версия (4) директивы #define определяет макрос-функцию с переменным количеством аргументов, но без обычных аргументов. К аргументам (называемым переменными аргументами) можно получить доступ только с помощью __VA_ARGS__ идентификатора, который затем заменяется аргументами, предоставленными с идентификатором, подлежащим замене.

Для версий (3,4), список-замен может содержать последовательность токенов __VA_OPT__(содержимое ), который заменяется содержимое, если __VA_ARGS__ не пустой, и расширяется до пустого в противном случае.

#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F()        // replaced by f(0)
 
#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)
G(a, b, c) // replaced by f(0, a, b, c)
G(a, )     // replaced by f(0, a)
G(a)       // replaced by f(0, a)
 
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
SDEF(foo);       // replaced by S foo;
SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 };
(с C++20)

Примечание: если аргумент макроса-функции включает запятые, которые не защищены парными открывающей и закрывающей скобками (чаще всего встречается в списках аргументов шаблонов, как в assert(std::is_same_v<int, int>); или BOOST_FOREACH(std::pair<int, int> p, m)), запятая интерпретируется как разделитель аргументов макроса, что приводит к ошибке компиляции из-за несоответствия количества аргументов.

Сканирование и замена
  • Сканирование отслеживает макросы, которые они заменили. Если сканирование находит текст, соответствующий такому макросу, оно помечает его как "для игнорирования" (все сканирования будут его игнорировать). Это предотвращает рекурсию.
  • Если сканирование находит макрос-функцию, аргументы сканируются перед их размещением внутри списка-замен. За исключением операторов # и ##, которые берут аргумент без сканирования.
  • После замены макроса, результирующий текст сканируется.

Обратите внимание, что возможно определить псевдорекурсивный макрос:

#define EMPTY
#define SCAN(x)     x
#define EXAMPLE_()  EXAMPLE
#define EXAMPLE(n)  EXAMPLE_ EMPTY()(n-1) (n)
EXAMPLE(5)
SCAN(EXAMPLE(5))

Вывод:

EXAMPLE_ ()(5 -1) (5)
EXAMPLE_ ()(5 -1 -1) (5 -1) (5)

Зарезервированные имена макросов

Единица трансляции, которая включает заголовок стандартной библиотеки, не может #define или #undef имена, объявленные в любом заголовке стандартной библиотеки.

Единица трансляции, использующая любую часть стандартной библиотеки, не может #define или #undef имена, лексически идентичные:

  • ключевым словам
  • идентификаторам со специальным значением
  • любому стандартному атрибуту, за исключением того, что likely и unlikely могут быть определены как макросы-функции(с C++20)
(с C++11)

В противном случае поведение неопределено.

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

В макросах-функциях оператор # перед идентификатором в списке-замен обрабатывает идентификатор с заменой параметров и помещает результат в кавычки, фактически создавая строковый литерал. Кроме того, предпроцессор добавляет обратные слэши для экранирования кавычек, окружающих вложенные строковые литералы, если таковые имеются, и удваивает обратные слэши внутри строки по мере необходимости. Все начальные и конечные пробелы удаляются, а любая последовательность пробелов посередине текста (но не внутри вложенных строковых литералов) сводится к одному пробелу. Эта операция называется "строковой интерпретацией". Если результат строковой интерпретации не является допустимым строковым литералом, поведение неопределено.

Когда # появляется перед __VA_ARGS__, весь расширенный __VA_ARGS__ заключается в кавычки:

#define showlist(...) puts(#__VA_ARGS__)
showlist();            // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int")
(с C++11)

Оператор ## между любыми двумя последовательными идентификаторами в списке-замен выполняет замену параметров для двух идентификаторов (которые не расширяются макросом предварительно) и затем соединяет результат. Эта операция называется "конкатенацией" или "склеиванием токенов". Могут склеиваться только токены, образующие вместе допустимый токен: идентификаторы, образующие более длинный идентификатор, цифры, образующие число, или операторы + и = образующие +=. Комментарий не может быть создан путем склеивания / и *, потому что комментарии удаляются из текста до того, как рассматривается замена макроса. Если результат конкатенации не является допустимым токеном, поведение неопределено.

Примечание: некоторые компиляторы предлагают расширение, которое позволяет ## появляться после запятой и перед __VA_ARGS__, в этом случае ## ничего не делает при наличии переменных аргументов, но удаляет запятую, когда переменные аргументы отсутствуют: это позволяет определять макросы, такие как fprintf (stderr, format, ##__VA_ARGS__). Это также можно достичь стандартным способом, используя __VA_OPT__, например fprintf (stderr, format __VA_OPT__(, ) __VA_ARGS__).(с C++20)

Директива #undef

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

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

Следующие имена макросов предопределены в каждой единице трансляции:

__cplusplus
обозначает используемую версию стандарта C++, расширяется до значения
  • 199711L(до C++11),
  • 201103L(C++11),
  • 201402L(C++14),
  • 201703L(C++17),
  • 202002L(C++20), или
  • 202302L(C++23)
    (макро-константа)
__STDC_HOSTED__
(C++11)
расширяется до целочисленной константы 1 , если реализация размещена (работает под ОС), ​0​ , если выполняется в автономном режиме (без ОС)
(макро-константа)
__FILE__
расширяется до имени текущего файла в виде строковой литералы, может быть изменено директивой #line
(макро-константа)
__LINE__
расширяется до номера строки исходного файла, целочисленной константы, может быть изменено директивой #line
(макро-константа)
__DATE__
расширяется до даты трансляции, строковой литералы формата "Mmm dd yyyy". Первый символ "dd" — пробел, если день месяца меньше 10. Название месяца формируется как если бы это было сделано std::asctime()
(макро-константа)
__TIME__
расширяется до времени трансляции, строковой литералы формата "hh:mm:ss"
(макро-константа)
__STDCPP_DEFAULT_NEW_ALIGNMENT__
(C++17)
расширяется до std::size_t литерала, значение которого соответствует выравниванию, гарантируемому вызовом operator new без учёта выравнивания (большие значения выравнивания будут переданы в перегрузку с учётом выравнивания, например, operator new(std::size_t, std::align_val_t))
(макро-константа)
__STDCPP_­BFLOAT16_­T____STDCPP_­FLOAT16_­T____STDCPP_FLOAT32_T____STDCPP_FLOAT64_T____STDCPP_FLOAT128_T__
(C++23)
расширяется до 1 только в том случае, если реализация поддерживает соответствующий расширенный тип плавающей точки
(макро-константа)

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

__STDC__
значение, определённое реализацией, которое обычно используется для указания соответствия стандарту C
(макро-константа)
__STDC_VERSION__
(C++11)
значение, определённое реализацией
(макро-константа)
__STDC_ISO_10646__
(C++11)

расширяется до целочисленной константы формата yyyymmL, если wchar_t использует Unicode, дата указывает на последнюю ревизию поддерживаемого Unicode

(до C++23)

значение, определённое реализацией

(с C++23)

(макро-константа)
__STDC_MB_MIGHT_NEQ_WC__
(C++11)
расширяется до 1, если 'x' == L'x' может быть ложным для члена базового набора символов, например, на системах на базе EBCDIC, которые используют Unicode для wchar_t
(макро-константа)
__STDCPP_THREADS__
(C++11)
расширяется до 1, если программа может иметь более одной потоковой нити выполнения
(макро-константа)
__STDCPP_STRICT_POINTER_SAFETY__
(C++11)(удалено в C++23)
расширяется до 1, если реализация имеет строгую std::pointer_safety
(макро-константа)

Значения этих макросов (кроме __FILE__ и __LINE__) остаются постоянными на протяжении всего блока трансляции. Попытки переопределения или удаления этих макросов приводят к неопределённому поведению.

Примечание: в теле каждой функции существует специальная локальная переменная с предварительным определением, названная __func__, определённая как статический символьный массив, содержащий имя функции в формате, определённом реализацией. Это не макрос препроцессора, но он используется совместно с __FILE__ и __LINE__, например, assert.

(с C++11)

Макросы для тестирования языковых функций

Стандарт определяет набор макросов препроцессора, соответствующих языковым функциям C++, представленным в C++11 или более поздних версиях. Они предназначены для простого и портативного обнаружения наличия этих функций.

См. Тестирование функций для получения подробной информации.

(с C++20)

Пример

#include <iostream>
 
// Make function factory and use it
#define FUNCTION(name, a) int fun_##name() { return a; }
 
FUNCTION(abcd, 12)
FUNCTION(fff, 2)
FUNCTION(qqq, 23)
 
#undef FUNCTION
#define FUNCTION 34
#define OUTPUT(a) std::cout << "output: " #a << '\n'
 
// Using a macro in the definition of a later macro
#define WORD "Hello "
#define OUTER(...) WORD #__VA_ARGS__
 
int main()
{
    std::cout << "abcd: " << fun_abcd() << '\n';
    std::cout << "fff: " << fun_fff() << '\n';
    std::cout << "qqq: " << fun_qqq() << '\n';
 
    std::cout << FUNCTION << '\n';
    OUTPUT(million); //note the lack of quotes
 
    std::cout << OUTER(World) << '\n';
    std::cout << OUTER(WORD World) << '\n';
}

Вывод:

abcd: 12
fff: 2
qqq: 23
34
output: million
Hello World
Hello WORD World

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применено к Поведение, как опубликовано Корректное поведение
LWG 294 C++98 блок трансляции, включающий заголовок стандартной библиотеки, мог содержать
макросы, которые определяют имена, объявленные в других заголовках стандартной библиотеки
запрещено
P2621R2 C++23 универсальные имена символов не допускались
формироваться путём конкатенации токенов
допускается

См. также

C++ документация для Индекс макросов
C документация для Замена текстовых макросов

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/preprocessor/replace

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API