проблема о другом отношении к __ VA_ARGS __ при использовании VS 2008 и GCC

Я пытаюсь определить проблему из-за необычного использования variadic макросов. Вот гипотетический макрос:

#define va(c, d, ...) c(d, __VA_ARGS__)
#define var(a, b, ...)  va(__VA_ARGS__, a, b)

var(2, 3, printf, “%d %d %d\n”, 1);

Для gcc препроцессор произведет

printf("%d %d %d\n", 1, 2, 3)

но для VS 2008, вывод

printf, “%d %d %d\n”, 1(2, 3);

Я подозреваю, что различие вызывается другим отношением к VA_ARGS для gcc, это сначала развернет выражение до va (printf, "%d %d %d\n", 1, 2, 3), и рассматривайте 1, 2, 3 как VA_ARGS для макроса va. Но для VS 2008, это будет сначала рассматривать b как VA_ARGS для макроса va и затем делать расширение.

Какой является корректной интерпретацией для макроса C99 variadic? или мое использование попадает в неопределенное поведение?

7
задан liuliu 4 April 2010 в 21:01
поделиться

1 ответ

Загляните в ISO/IEC 9899:1999, глава 6.10.3.1. Там говорится, что:

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

Так в va первый аргумент c имеет один токен препроцессирования - __VA_ARGS__, который, согласно этому параграфу, должен быть макрозаменен перед подстановкой в c (что по-прежнему не дает ответа на вопрос, какой компилятор прав)

Но далее:

Идентификатор __VA_ARGS__, который встречается в списке замены должен рассматриваться как параметр, а переменные аргументы должны формировать лексемы предварительной обработки, используемые для замены.

Согласно первому фрагменту, сначала определяются аргументы var. Ими являются: 2, 3, printf, "%d %d %d\n" и 1. Теперь происходит подстановка аргументов. Это означает, что параметры из списка замены var берутся и заменяются. Однако во втором фрагменте указано, что идентификатор __VA_ARGS__ должен рассматриваться как параметр, поэтому он должен быть заменен на printf, "%d %d %d\n", 1. Теперь внутри параметров нет макросов, поэтому дальнейшей подстановки не происходит, в результате чего var(2, 3, printf, "%d %d %d\n", 1); расширяется до va(printf, "%d %d %d\n", 1, 2, 3);. Поскольку va является макросом, он также расширяется, давая результат printf("%d %d %d\n", 1, 2, 3);. Теперь, если принять рассуждения VS 2008, как она может определить аргументы для va, если один из них __VA_ARGS__ из var, и может содержать много аргументов? Ну, он рассматривает __VA_ARGS__ как аргумент к va, что, на мой взгляд, неверно, поскольку, согласно первому фрагменту, подстановка аргументов происходит только после определения аргументов для вызова.

Поэтому мне кажется, что в var препроцессор должен сначала определить аргументы для вызова макроса va, а затем начать расширение va. А это значит, что, вероятно, gcc здесь прав.

Также в C preprocessor and concatenation показано, как обрабатывающие лексемы заменяются макросами по порядку, пока не останется идентификаторов, которые могут быть далее расширены как макросы, или препроцессор обнаружит рекурсию и прекратит расширение.

4
ответ дан 7 December 2019 в 01:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: