Я пытаюсь написать программу, в которой имена некоторых функций зависят от значения определенной макропеременной с помощью макроса:
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
int NAME(some_function)(int a);
К сожалению, макрос NAME()
превращает это в
int some_function_VARIABLE(int a);
скорее, чем
int some_function_3(int a);
так что это явно неправильный путь. К счастью, число различных возможных значений для VARIABLE невелико, поэтому я могу просто сделать #if VARIABLE == n
и перечислить все случаи отдельно, но мне было интересно, если есть разумный способ сделать это.
#define A 0 \n #define M a ## A
: наличие двух##
не является ключом.Ответы:
Стандартный C препроцессор
Два уровня косвенности
В комментарии к другому ответу Кейд Ру спросил, почему для этого нужны два уровня косвенности. Непростой ответ: потому что так требует стандарт для работы; вы также обнаружите, что вам нужен эквивалентный трюк с оператором stringizing.
Раздел 6.10.3 стандарта C99 охватывает «замену макросов», а 6.10.3.1 - «замену аргументов».
В вызове
NAME(mine)
аргумент «мой»; оно полностью расширено до «моего»; затем он подставляется в строку замены:Теперь макрос EVALUATOR обнаружен, а аргументы изолированы как «моя» и «переменная»; последний затем полностью расширяется до «3» и подставляется в строку замены:
Операция этого покрыта другими правилами (6.10.3.3 «Оператор ##»):
Итак, список замены содержит,
x
а затем##
и##
следуютy
; итак имеем:и удаление
##
токенов и объединение токенов с обеих сторон объединяет «мой» с «_» и «3» для получения:Это желаемый результат.
Если мы посмотрим на исходный вопрос, код был (адаптирован для использования 'mine' вместо 'some_function'):
Аргумент NAME явно «мой», и он полностью расширен.
Следуя правилам 6.10.3.3, находим:
который, когда
##
операторы исключены, отображается на:именно так, как сообщается в вопросе.
Традиционный препроцессор C
Роберт Рюгер спрашивает :
Возможно, а может и нет - это зависит от препроцессора. Одно из преимуществ стандартного препроцессора состоит в том, что он имеет эту функцию, которая работает надежно, тогда как для предстандартных препроцессоров были разные реализации. Одно требование состоит в том, что, когда препроцессор заменяет комментарий, он не генерирует пробел, как это требуется для препроцессора ANSI. Препроцессор GCC (6.3.0) C отвечает этому требованию; препроцессор Clang из XCode 8.2.1 этого не делает.
Когда это работает, это делает работу (
x-paste.c
):Обратите внимание, что между
fun,
иVARIABLE
- нет пробела , это важно, потому что, если оно присутствует, оно копируется в вывод, и в результате выmine_ 3
получаете имя, которое, конечно, не является синтаксически допустимым. (Теперь, пожалуйста, можно мне вернуть волосы?)С GCC 6.3.0 (работает
cpp -traditional x-paste.c
) я получаю:С Clang из XCode 8.2.1 я получаю:
Эти пространства все портят. Я отмечаю, что оба препроцессора верны; разные предстандартные препроцессоры демонстрировали оба поведения, что делало вставку токенов чрезвычайно раздражающим и ненадежным процессом при попытке переноса кода. Стандарт с
##
обозначениями радикально упрощает это.Там могут быть другие способы сделать это. Однако это не работает:
GCC генерирует:
Близко, но без игры в кости. YMMV, конечно, в зависимости от используемого вами стандартного препроцессора. Честно говоря, если вы застряли с препроцессором, который не взаимодействует, вероятно, было бы проще организовать использование стандартного препроцессора C вместо предстандартного (обычно есть способ настроить компилятор соответствующим образом), чем тратить много времени, пытаясь найти способ сделать работу.
источник
cpp -traditional
. Обратите внимание, что нет однозначного ответа - это зависит от вашего препроцессора.Честно говоря, вы не хотите знать, почему это работает. Если вы знаете, почему это работает, вы станете тем парнем на работе, который знает подобные вещи, и все придут, чтобы задать вам вопросы. знак равно
Изменить: если вы действительно хотите знать, почему это работает, я с удовольствием выложу объяснение, предполагая, что никто не побьет меня.
источник