Зачем нужен двойной слой косвенности для макросов?

В: С++ FAQ - Различные технические проблемы - [39.6] Что делать с макросами, которым нужно вставить два токена вместе?

Может ли кто-нибудь объяснить мне почему? Все, что я прочитал, мне доверяют, но я просто не могу просто доверять чему-то, потому что кто-то сказал это.

Я пробовал этот подход, и я не могу найти никаких ошибок:

#define mymacro(a) int a ## __LINE__
mymacro(prefix) = 5;
mymacro(__LINE__) = 5;
int test = prefix__LINE__*__LINE____LINE__; // fine

Итак почему мне нужно сделать это так (цитата с веб-страницы):

Однако, если вы используете ##, вам нужен двойной слой косвенности. В основном вам нужно создать специальный макрос для "склейки маркеров" как:

 #define NAME2(a,b)         NAME2_HIDDEN(a,b)
 #define NAME2_HIDDEN(a,b)  a ## b 

Поверьте мне в этом - вам действительно нужно сделать это! (И, пожалуйста, никто не напишет мне, что это иногда работает без второй слой косвенности. Попробуйте конкатенировать символ с помощью __ LINE__ и посмотрим, что произойдет потом.)

Изменить: Может ли кто-то объяснить, почему он использует NAME2_HIDDEN, прежде чем он будет указан ниже? Кажется более логичным определить макрос NAME2_HIDDEN, прежде чем использовать его. Это какой-то трюк здесь?

Ответ 1

Соответствующая часть спецификации C:

6.10.3.1 Подстановка аргумента

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

Ключевая часть, которая определяет, хотите ли вы двойное косвенное или нет, - это второе предложение и исключение в нем - если параметр задействован в операции # или ## (например, параметры в mymacro и NAME2_HIDDEN), то любые другие макросы в аргументе НЕ расширяются до выполнения # или ##. Если, с другой стороны, там нет # или ## НЕМЕДЛЕННО в теле макроса (как в случае с NAME2), тогда расширяются другие макросы в параметрах ARE.

Итак, все зависит от того, что вы хотите - иногда вы хотите, чтобы все макросы были расширены FIRST, а затем выполните # или ## (в этом случае вы хотите двойное наложение), а иногда вы НЕ хотите макросы сначала раскрываются (в этом случае вы НЕ МОЖЕТЕ иметь макросы с двойным слоем, вам нужно сделать это напрямую.)

Ответ 2

__LINE__ - это специальный макрос, который должен разрешить текущий номер строки. Однако, когда вы делаете палку с токеном с __LINE__ напрямую, она не получает возможности для решения, поэтому вы получаете токен prefix__LINE__ вместо, скажем, prefix23, например, вы, вероятно, ожидаете, если вы должны написать этот код в дикой природе.

Ответ 3

У Криса Додда есть отличное объяснение первой части вашего вопроса. Что касается второй части, о последовательности определения, то короткая версия заключается в том, что директивы #define сами по себе не оцениваются вообще; они только оцениваются и расширяются, когда символ находится в другом месте файла. Например:

#define A a  //adds A->a to the symbol table
#define B b  //adds B->b to the symbol table

int A;

#undef A     //removes A->a from the symbol table
#define A B  //adds A->B to the symbol table

int A;

Первый int A; становится int A;, потому что именно так A определяется в этой точке файла. Второй int A; становится int b; после двух разложений. Он сначала расширен до int b;, потому что A определяется как B в этой точке файла. Препроцессор затем распознает, что B является макросом, когда он проверяет таблицу символов. B затем расширяется до B.

Единственное, что имеет значение, это определение символа в точке расширения, независимо от того, где это определение.

Ответ 4

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