В: С++ 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
Порядок, в котором объявлены макросы, не имеет значения, порядок, в котором они используются. Если бы вы фактически использовали этот макрос до его объявления - (в фактическом коде, а не в макросе, который остается бездействующим до вызова), вы получите ошибку, но поскольку большинство здравомыслящих людей не ходит вокруг, такие вещи, писать макрос, а затем писать функцию, которая использует макрос, еще не определенный ниже, и т.д. и т.д. Кажется, ваш вопрос - это не только один вопрос, но я просто отвечу на эту часть. Я думаю, вам следовало бы сломать это немного больше.