Можно ли вкладывать директивы препроцессора C?

Например, возможно ли следующее:

#define definer(x) #define #x?

person mannicken    schedule 09.01.2009    source источник
comment
Из любопытства, зачем вам это нужно?   -  person    schedule 09.01.2009
comment
одно использование позволяет CPP оценить оператор дважды в разных контекстах. У меня есть система, которая использует grep/sed для расширения векторных уравнений, делая это.   -  person BCS    schedule 09.01.2009


Ответы (6)


Хотя ваш синтаксис неверен, ответ на ваш вопрос технически да. Но этого можно добиться только с помощью неприятных уловок, которые сделают ваш код нечитаемым и непригодным для сопровождения.

См. также: http://www.ioccc.org/years.html#1995_vanschnitz и http://www.ioccc.org/years.html#2004_vik2

person HUAGHAGUAH    schedule 10.01.2009
comment
Я не вижу "Да" в ссылках... только странный запутанный код C, не могли бы вы привести пример этого? - person Hernán Eche; 01.06.2010

Нет, вы не можете этого сделать.
Символ фунта стерлингов (#) имеет другое значение в определении. это означает - если это аргумент, сделайте его строкой, заключив в кавычки.

person shoosh    schedule 09.01.2009
comment
Технический термин для этого — строковый оператор (а ## — оператор вставки токена). - person Adam Rosenfield; 09.01.2009
comment
Эээ... вы видели в последнее время assert()? В простейшей форме: #define assert(expr) if (!(expr)) { printf(Утверждение не удалось (%s) в файле '%s', строка %d.\n, #expr, ФАЙЛ, LINE); abort();} Так что, действительно, вы можете, однако это злоупотребление препроцессором. Если вы помните, как вы это называете ... все в порядке - person Tim Post♦; 09.01.2009
comment
Кроме того, комментируя безмозглость assert, если вы не можете выделить 10 байтов, assert() также обречен на неудачу. Плохой пример из реальной жизни, но иллюстрирующий мою точку зрения. - person Tim Post♦; 09.01.2009
comment
Я не понимаю, как утверждение даже отдаленно связано с этим ответом или вопросом. - person shoosh; 09.01.2009
comment
Этот макрос assert на самом деле использует оператор строковой #. #expr создает C-строку аргумента для assert(expr). - person Mr.Ree; 09.01.2009

Вы не можете вкладывать директивы препроцессора C. К счастью, это почти никогда не требуется. Если вам действительно нужна такая мощность, вам почти наверняка будет лучше использовать другой препроцессор, который вы запускаете перед передачей кода компилятору C. Например:

sed 's/@CUSTOMER@/J. Random Person/' foo.c.in > foo.c
cc foo.c

Еще один полезный прием — изолировать обман в один заголовочный файл, который генерируется программой, которую вы пишете сами:

./generate-trickery --greet 'J. Random Person' > foo.h

где foo.h будет выглядеть примерно так:

#define GREET(x) ("J. Random Person greets you, " #x)

Если вы свяжете это вместе с Makefile или какой-либо другой автоматизацией, это будет довольно гладко и не сильно затруднит вашу разработку.

person Community    schedule 09.01.2009

Нет, ты не можешь этого сделать.

Вы можете ссылаться на один макрос из другого, но вы не можете определить один макрос из другого.

person qrdl    schedule 09.01.2009

Если вы пытаетесь создать сегмент кода препроцессора, который можно вызывать несколько раз для выполнения немного разных действий, один (умеренно ужасный) способ сделать это — изолировать код в один файл .h, который вы затем #include несколько раз. Идея состоит в том, что каждый раз, когда вы #include вызываете файл, вы "вызываете свою процедуру" -- вы "передаете аргументы", сначала #define вводя определенные константы препроцессора, на которые ссылается включенный файл.

Одно место, где я видел, что это полезно, - это создание "умных" перечислений, которые могут преобразовываться в/из их "строковых" форм (что полезно для ввода-вывода). Вы создаете файл .h, содержащий, например.

ENUMVAL(foo)
ENUMVAL(bar)
ENUMVAL(baz)

а затем позже #include этот файл дважды: один раз, когда ENUMVAL() определяется таким образом, чтобы создать объявление enum, и один раз, когда ENUMVAL() определяется таким образом, чтобы создать массив строковых имен. Делая это таким образом, вам не нужно указывать список фактических токенов более одного раза.

person j_random_hacker    schedule 09.01.2009
comment
Эта техника называется X-Macro. - person qrdl; 09.01.2009
comment
Ах! Я не знал, что у него есть имя. Спасибо! :) - person j_random_hacker; 09.01.2009
comment
@qrdl и j_random_hacker: +1 за X-Macro. Прочитал эту статью на сайте Dr.Dobbs и нашел ее полезной в некоторых ситуациях. с участием разных частей кода со списками параметров с похожими именами. Также есть эта статья, которая объясняет немного больше, но показывает меньше кода. - person Guarita; 22.11.2012

#define definer(x) #define #x?

#x — это строковое обозначение x. Вы не можете #define строковый токен. (#define "foo".) Это должен быть токен идентификатора [a-zA-Z0-9_]*.

Вы не можете вкладывать #define таким образом. У вас не может быть #define в #define.

Вы можете иметь #if внутри блоков #if.

#ifdef FOO

#ifdef BAR
 ...
#else // BAR
 ...
#endif // BAR

#else // FOO
 ...
#endif //FOO

Вы также несколько ограничены в выражениях, которые вы можете использовать в макросах #if. Но иногда можно обойти это. Например:

        /* Use CONCATENATE
STATIC_ASSERT( sizeof(int1) == 1, sizeof_int1_equal_1 );
AGAIN to expand the arguments to CONCATENATE_4 */ #define CONCATENATE_4( a,b,c,d) CONCATENATE
STATIC_ASSERT( sizeof(int1) == 1, sizeof_int1_equal_1 );
AGAIN(a,b,c,d) #define CONCATENATE
STATIC_ASSERT( sizeof(int1) == 1, sizeof_int1_equal_1 );
AGAIN(a,b,c,d) a ## b ## c ## d /* Creates a typedef that's legal/illegal depending on EXPRESSION. * * Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*". * * (This may be replaced by static_assert() in future revisions of C++.) */ #define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT) \ typedef char CONCATENATE_4( static_assert____, IDENTIFIER_TEXT, \ ____failed_at_line____, __LINE__ ) \ [ (EXPRESSION) ? 1 : -1 ]

Плюс что-то вроде:

STATIC_ASSERT( sizeof(int1) == 1, sizeof_int1_equal_1 );

(Да, я знаю о #include ‹stdint.h›. Это просто пример.)

person Mr.Ree    schedule 09.01.2009