Spec-Zone.ru › C++

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

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

Синтаксис

#define identifier replacement-list (необязательно) (1)
#define identifier (parameters ) replacement-list (необязательно) (2)
#define identifier (parameters , ...) replacement-list (необязательно) (3) (начиная с C++11)
#define identifier (...) replacement-list (необязательно) (4) (начиная с C++11)
#undef identifier (5)

Пояснение

Директивы #define

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

Если identifier уже определён как макрос другой директивой #defined, программа некорректна, если только определения не идентичны.

Если identifier является именем предопределённого макроса (см. ниже) или идентификатором defined, поведение не определено(до C++26)программа некорректна(начиная с C++26).

Макросы, подобные объектам

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

Макросы, подобные функциям

Макросы, подобные функциям, заменяют каждое вхождение определённого identifier на replacement-list, дополнительно принимая несколько аргументов, разделённых запятыми; эти аргументы затем заменяют соответствующие вхождения любых parameters в replacement-list. Если аргументы содержат директиву препроцессора, поведение не определено(до C++26)программа некорректна(начиная с C++26).

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

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

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

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

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

Для версий (3,4) replacement-list может содержать последовательность токенов __VA_OPT__(content ), которая заменяется на content, если __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)), запятая интерпретируется как разделитель аргументов макроса, что приводит к ошибке компиляции из-за несовпадения числа аргументов.

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

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

#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)

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

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

В макросах, подобных функциям, оператор #, стоящий перед идентификатором в replacement-list, заменяет параметр в этом идентификаторе и заключает результат в кавычки, фактически создавая строковый литерал без префикса.

Назовём стрингифицируемым аргументом последовательность токенов препроцессора, полученную после расширения идентификатора. Если стрингифицируемый аргумент пуст, результатом является пустой строковый литерал. В противном случае:

  • Удаляются пробельные символы перед первым токеном препроцессора и после последнего токена препроцессора, входящих в стрингифицируемый аргумент.
  • Каждая последовательность пробельных символов между токенами препроцессора стрингифицируемого аргумента становится одним символом пробела в строковом литерале.
  • Исходное написание каждого токена препроцессора в стрингифицируемом аргументе сохраняется в строковом литерале, за исключением обработки имён заголовков, символьных литералов и строковых литералов:
    • Перед каждым существующим символом " и символом \ вставляется символ обратной косой черты \.
    • Каждый символ новой строки заменяется двухсимвольной последовательностью \n.

Если результат не является допустимым строковым литералом, поведение не определено(до C++26)программа некорректна(начиная с C++26).

Если перед __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)

Оператор ##, расположенный между любыми двумя соседними идентификаторами в replacement-list, заменяет параметры в обоих идентификаторах (которые предварительно не раскрываются как макросы), а затем объединяет результат.

Объединение может привести к образованию новых токенов препроцессора: созданию более длинного идентификатора из коротких идентификаторов, образованию числа из цифр или образованию оператора += из операторов + и =. Комментарий нельзя образовать объединением / и *, поскольку комментарии удаляются из текста до подстановки макросов.

Если результат объединения не является допустимым токеном препроцессора, поведение не определено(до C++26)программа некорректна(начиная с C++26).

Директива #undef

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

Если identifier является именем предопределённого макроса (см. ниже) или идентификатором defined, поведение не определено(до C++26)программа некорректна(начиная с C++26).

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

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

__cplusplus
обозначает используемую версию стандарта C++, раскрывается в значение
  • 199711L(до C++11),
  • 201103L(C++11),
  • 201402L(C++14),
  • 201703L(C++17),
  • 202002L(C++20),
  • 202302L(C++23) или
  • 202603L(C++26)
    (константа макроса)
__STDC_HOSTED__
(C++11)
раскрывается в целочисленную константу 1, если реализация является hosted (работает под управлением ОС), и 0, если реализация freestanding (работает без ОС)
(константа макроса)
__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)). Выравнивание, более строгое, чем требуемое этим макросом, называется расширенным выравниванием для new.
(константа макроса)
__STDCPP_­BFLOAT16_­T____STDCPP_­FLOAT16_­T____STDCPP_FLOAT32_T____STDCPP_FLOAT64_T____STDCPP_FLOAT128_T__
(C++23)
раскрывается в 1 тогда и только тогда, когда реализация поддерживает соответствующий расширенный тип с плавающей точкой
(константа макроса)
__STDC_EMBED_NOT_FOUND____STDC_EMBED_FOUND____STDC_EMBED_EMPTY__
(C++26)
раскрывается соответственно в 0, 1 и 2
(константа макроса)

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

__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' может быть ложным для элемента x базового набора символов, например в системах на основе EBCDIC, использующих Unicode для wchar_t
(константа макроса)
__STDCPP_THREADS__
(C++11)
раскрывается в 1, если программа может иметь более одного потока выполнения
(константа макроса)
__STDCPP_STRICT_POINTER_SAFETY__
(C++11)(удалён в C++23)
раскрывается в 1, если реализация обеспечивает строгую std::pointer_safety
(константа макроса)

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

Макросы проверки языковых возможностей

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

Подробности см. в разделе Проверка возможностей.

(начиная с C++20)

Примечания

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

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

(начиная с C++11)

Пример

#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 Применено к Опубликованное поведение Корректное поведение
CWG 2908 C++98 было неясно, раскрывается ли __LINE__ в номер текущей
физической строки или текущей логической строки
раскрывается в номер текущей
физической строки
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/cpp/preprocessor/replace

Spec-Zone.ru

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