Как расширить определение макросов во время конкатенации? [Дубликат]

У меня была такая же проблема с использованием http-сервера как моего локального веб-сервера, его известной проблемы с html5 и подробностей в угловом ui / ui-router FAQ .

Я переключился на Nginx (установите через apt-get) со следующим /etc/nginx/sites-enabled/default

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    #root /var/www/html;

    root /home/conor/workspace/code_institute/route_test;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    #server_name _;
    server_name localhost;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.html;
    }

 }

. Для запуска / остановки / перезагрузки после изменений;

sudo service nginx 

Apache / Экспресс / asp.NET

125
задан Ciro Santilli 新疆改造中心 六四事件 法轮功 21 June 2015 в 09:35
поделиться

2 ответа

Стандартный препроцессор 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 вместо предстандартного (обычно есть способ правильно настроить компилятор), чем потратьте много времени, пытаясь выработать способ выполнить эту работу.

192
ответ дан Community 23 August 2018 в 06:58
поделиться
  • 1
    Да, это решает проблему. Я знал трюк с двумя уровнями рекурсии - мне приходилось играть с натяжением хотя бы один раз, но не знал, как это сделать. – JJ. 29 September 2009 в 04:34
  • 2
    Отличное спасибо! – malhal 18 May 2016 в 11:25
  • 3
    Есть ли какой-либо способ сделать это с традиционным препроцессором C , который не имеет оператора покраски маркера ##? – Robert Rüger 24 December 2016 в 01:03
  • 4
    @ RobertRüger: он удваивает длину ответа, но я добавил информацию для покрытия cpp -traditional. Обратите внимание, что нет окончательного ответа - это зависит от препроцессора, который у вас есть. – Jonathan Leffler 24 December 2016 в 03:40
  • 5
    Большое спасибо за ответ. Это совершенно здорово! Тем временем я также нашел другое, немного другое решение. См. здесь . У него также есть проблема, что он не работает с clang. К счастью, это не проблема для моего приложения ... – Robert Rüger 26 December 2016 в 12:03
#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);

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

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

29
ответ дан Stephen Canon 23 August 2018 в 06:58
поделиться
  • 1
    Не могли бы вы объяснить, почему ему нужны два уровня косвенности. У меня был ответ с одним уровнем перенаправления, но я удалил ответ, потому что мне пришлось установить C ++ в мою Visual Studio, и тогда это не сработало. – Cade Roux 29 September 2009 в 02:31
  • 2
    См. Самое прекрасное объяснение Джонатана Леффлера. – Stephen Canon 29 September 2009 в 16:26
Другие вопросы по тегам:

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