Можете ли вы вложить директивы препроцессора C? - PullRequest
4 голосов
/ 09 января 2009

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

#define definer(x) #define #x?

Ответы [ 6 ]

9 голосов
/ 09 января 2009

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

8 голосов
/ 09 января 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 или какой-либо другой автоматизацией, это будет довольно плавно и не сильно запутывает ваше развитие.

3 голосов
/ 09 января 2009

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

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

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

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

3 голосов
/ 09 января 2009

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

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

2 голосов
/ 09 января 2009
#define definer(x) #define #x?

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

Вы не можете nest # определить, как это. Вы не можете иметь #define в # define.

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

#ifdef FOO

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

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

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

        /* Use CONCATENATE_4_AGAIN to expand the arguments to CONCATENATE_4 */
#define CONCATENATE_4(      a,b,c,d)  CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_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 . Это всего лишь пример.)

0 голосов
/ 10 января 2009

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

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

...