Как объединить два раза с препроцессором C и развернуть макрос, как в "arg ## _ ## MACRO"? - программирование

Как объединить два раза с препроцессором C и развернуть макрос, как в "arg ## _ ## MACRO"?

Я пытаюсь написать программу, где имена некоторых функций зависят от значения определенной макропеременной с макросом вроде этого:

#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 и перечислить все случаи отдельно, но мне было интересно, есть ли у него разумный способ сделать это.

4b9b3361

Ответ 1

Стандартный препроцессор C

$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"





extern void mine_3(char *x);
$

Два уровня косвенности

В комментарии к другому ответу Cade Roux спросил, почему для этого нужны два уровня косвенности. Легкий ответ - это то, как стандарт требует от него работы; вы, как правило, обнаруживаете, что вам нужен эквивалентный трюк с помощью оператора стропинга.

Раздел 6.10.3 стандарта C99 охватывает "замену макросов", а 6.10.3.1 охватывает "замену аргументов".

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

В вызове NAME(mine) аргумент "мой" ; он полностью расширен до "моего"; он затем заменяется на заменяющую строку:

EVALUATOR(mine, VARIABLE)

Теперь обнаружен макрос EVALUATOR, и аргументы изолированы как "мои" и "ПЕРЕМЕННЫЕ"; последний затем полностью расширяется до "3" и заменяется на заменяющую строку:

PASTER(mine, 3)

Операция этого распространяется на другие правила (6.10.3.3 "Оператор ##" ):

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

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

Итак, список замены содержит x, за которым следует ##, а также ##, за которым следует y; поэтому мы имеем:

mine ## _ ## 3

и устранение токенов ## и объединение маркеров с обеих сторон объединяет "мой" с "_" и "3", чтобы получить:

mine_3

Это желаемый результат.


Если мы посмотрим на исходный вопрос, код был (адаптирован для использования "mine" вместо "some_function" ):

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

Аргумент NAME явно "мой" , и он полностью расширен.
Следуя правилам 6.10.3.3, находим:

mine ## _ ## VARIABLE

который при устранении операторов ## отображает:

mine_VARIABLE

как указано в вопросе.


Традиционный препроцессор C

Роберт Рюгер спрашивает:

Есть ли какой-либо способ сделать это с традиционным препроцессором C, который не имеет оператора для ввода токена ##?

Возможно, а может и нет - это зависит от препроцессора. Одним из преимуществ стандартного препроцессора является то, что он имеет этот механизм, который работает надежно, тогда как для пре-стандартных препроцессоров были разные реализации. Одним из требований является то, что, когда препроцессор заменяет комментарий, он не генерирует пробел, поскольку требуется препроцессор ANSI. Препроцессор GCC (6.3.0) C соответствует этому требованию; препроцессор Clang из XCode 8.2.1 не работает.

Когда он работает, это выполняет задание (x-paste.c):

#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

Обратите внимание, что между fun, и VARIABLE не существует пробела - это важно, потому что если он присутствует, он копируется на выход, и вы получаете mine_ 3 как имя, которое не является синтаксическим действительный, конечно. (Теперь, пожалуйста, я могу вернуть свои волосы?)

С GCC 6.3.0 (работает cpp -traditional x-paste.c) я получаю:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_3(char *x);

С Clang из XCode 8.2.1 я получаю:

# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2





extern void mine _ 3(char *x);

Эти пространства портят все. Я отмечаю, что оба препроцессора верны; разные пре-стандартные препроцессоры демонстрировали оба поведения, которые делали маркер, вставляя чрезвычайно раздражающий и ненадежный процесс при попытке переноса кода. Стандарт с обозначением ## радикально упрощает это.

Могут быть другие способы сделать это. Однако это не работает:

#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)

extern void NAME(mine)(char *x);

GCC генерирует:

# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"





extern void mine_VARIABLE(char *x);

Закрыть, но не кубики. YMMV, конечно, в зависимости от используемого вами пре-стандартного препроцессора. Честно говоря, если вы застряли в препроцессоре, который не сотрудничает, вероятно, будет проще организовать использование стандартного препроцессора C вместо предстандартного (обычно есть способ правильно настроить компилятор), чем потратьте много времени, пытаясь выработать способ выполнить эту работу.

Ответ 2

#define VARIABLE 3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun) NAME1(fun,VARIABLE)

int NAME(some_function)(int a);

Честно говоря, вы не хотите знать, почему это работает. Если вы знаете, почему это работает, вы станете этим парнем на работе, который знает такие вещи, и все придут задавать вам вопросы. =)

Изменить:, если вы действительно хотите знать, почему это работает, я с радостью опубликую объяснение, полагая, что меня никто не бьет.