3.5 Конкатенация
Часто бывает полезно объединить два токена в один при разворачивании макросов. Это называется склеиванием токенов или конкатенацией токенов. Оператор предварительной обработки ‘##’ выполняет склеивание токенов. Когда макрос разворачивается, два токена по обе стороны от каждого оператора ‘##’ объединяются в один токен, который затем заменяет оператор ‘##’ и два исходных токена в развёртывании макроса. Обычно оба будут идентификаторами, или один будет идентификатором, а другой — числом предварительной обработки. При склеивании они образуют более длинный идентификатор. Это не единственный допустимый случай. Также возможно конкатенировать два числа (или число и имя, например 1.5 и e3) в число. Кроме того, можно образовывать многосимвольные операторы, такие как +=, посредством конкатенации токенов.
Однако два токена, которые вместе не образуют допустимого токена, не могут быть склеены. Например, вы не можете конкатенировать x с + ни в каком порядке. Если вы попытаетесь, препроцессор выдаст предупреждение и выведет два токена. Определено ли, будут ли между токенами пробелы, не определено. Нередко встречаются избыточные применения ‘##’ в сложных макросах. Если вы получаете такое предупреждение, скорее всего, вы можете просто удалить ‘##’.
Оба токена, объединённые с помощью ‘##’, могут быть взяты из тела макроса, но вы могли бы также записать их как один токен с самого начала. Конкатенация токенов наиболее полезна, когда один или оба токена берутся из аргумента макроса. Если любой из токенов рядом с ‘##’ является именем параметра, он заменяется его фактическим аргументом до выполнения ‘##’. Как и при строковом представлении, фактический аргумент сначала не разворачивается макросом. Если аргумент пустой, этот ‘##’ не оказывает никакого влияния.
Помните, что препроцессор C преобразует комментарии в пробелы ещё до того, как будут рассматриваться макросы. Поэтому вы не можете создать комментарий, склеив ‘/’ и ‘*’. Вы можете вставлять столько пробелов между ‘##’ и его операндами, сколько вам нужно, включая комментарии, и вы можете вставлять комментарии в аргументы, которые будут конкатенированы. Однако появление ‘##’ в начале или конце тела макроса является ошибкой.
Представьте себе программу на C, которая интерпретирует именованные команды. Вероятно, потребуется таблица команд, возможно, массив структур, объявленных следующим образом:
struct command
{
char *name;
void (*function) (void);
};struct command commands[] =
{
{ "quit", quit_command },
{ "help", help_command },
…
};Было бы лучше не приводить каждое имя команды дважды, один раз в строковой константе и один раз в имени функции. Макрос, принимающий имя команды в качестве аргумента, может сделать это ненужным. Строковая константа может быть создана с помощью строкового представления, а имя функции — путём конкатенации аргумента с ‘_command’. Вот как это делается:
#define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
…
};
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-14.2.0/cpp/Concatenation.html