проблема о различном обращении к __VA_ARGS__ при использовании VS 2008 и GCC - PullRequest
7 голосов
/ 05 апреля 2010

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

#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? или мое использование попадает в неопределенное поведение?

Ответы [ 2 ]

6 голосов
/ 21 октября 2010

Существует простой способ справиться с этой проблемой:

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

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

Это сделает трюк на VS 2008 и не повлияет на gcc

4 голосов
/ 05 апреля 2010

Посмотрите 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 препроцессоре и конкатенации , как обрабатываются токены, заменяемые макросами по порядку, пока не будет идентификаторов, которые могут быть дополнительно расширены как макросы, или препроцессор обнаружит рекурсию и завершит расширение .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...