Могу ли я добавить к макросу препроцессора? - PullRequest
9 голосов
/ 29 декабря 2010

Есть ли способ в стандартном C - или с расширениями GNU - добавить материал в определение макроса? Например, , если макрос определен как
#define List foo bar
, могу ли я добавить bas, чтобы он List расширялся, как если бы я его определил
#define List foo bar bas?

Я надеялся, что смогу сделать что-то вроде этого:

#define List    foo bar bas

#define List_   Expand(List)
#undef List
#define List    Expand(List_) quux

, но я не могу понять, как определить макрос Expand(), чтобы он делал то, что я хочу.

Мотивация: Я играю с дискриминированными / помеченными профсоюзами по следующим линиям:

struct quux_foo { int x; };
struct quux_bar { char *s; };
struct quux_bas { void *p; };

enum quux_type {quux_foo, quux_bar, quux_bas};

struct quux {
    enum quux_type type;
    union {
        struct quux_foo foo;
        struct quux_bar bar;
        struct quux_bas bas;
    } t;
};

Я считаю, что это хорошее место для X-макроса.Если я определю макрос
#define quux_table X(foo) X(bar) X(bas)
, перечисление и структура могут быть определены таким образом, и никогда не выйдут из синхронизации:

#define X(t) quux_ ## t,
enum quux_type {quux_table};
#undef X

#define X(t) struct quux_ ## t t;
struct quux {
    enum quux_type type;
    union {quux_table} t;
};
#undef X

Конечно, структуры quux_* могут выйтисинхронизации, поэтому я хотел бы сделать что-то вроде этого, только на законных основаниях:

struct quux_foo { int x; };
#define quux_table quux_table X(foo)

struct quux_bar { char *s; };
#define quux_table quux_table X(bar)

struct quux_bas { void *p; };
#define quux_table quux_table X(bas)

(Ну, то, что я действительно хочу сделать, это что-то вроде
member_struct(quux, foo) { int x; };
но я хорошо знаю, что макросы нельзя (пере) определять из макросов.)

Во всяком случае, это мой мотивирующий пример.Есть ли способ сделать это?

Примеры Boost.Preprocessor хороши, если вы можете показать мне, как заставить технику X-macro работать с этой библиотекой.

Ответы [ 3 ]

6 голосов
/ 30 декабря 2010

Фактически, нет.

Макросы лениво оцениваются.Когда вы #define List_ Expand(List), его список замен представляет собой последовательность из четырех токенов Expand, (, List и ).Нет никакого способа расширить макрос в список замены.

Замена всех макросов происходит при запуске макроса.

Я бы рекомендовал использовать библиотеку Boost.Preprocessor для автоматической генерации кода.Это немного работы, но вы можете выполнить некоторые довольно впечатляющие вещи , используя его.Он должен быть полностью совместим с C.

5 голосов
/ 18 августа 2017

Есть способ!

Используя новое ключевое слово _Pragma, это может быть достигнуто в gcc (хотя и не с msvc)

Если вы вставите макрос в его собственное определение, это задержит егорасширение, пока макрос не будет раскрыт в первый раз.Это позволяет сделать его предыдущее расширение частью его собственного определения.Однако, поскольку он всплывает во время расширения, его можно использовать только один раз

. Вот пример кода, чтобы увидеть его в действии

#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience
#define popfoo _Pragma("pop_macro(\"foo\")")

#define foo 1

pushfoo                           //push the old value
#undef foo                        //so you don't get a warning on the next line
#define foo popfoo foo , 2        //append to the previous value of foo

pushfoo
#undef foo
#define foo popfoo foo , 3

pushfoo
#undef foo
#define foo popfoo foo , 4


foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4
    //which will in turn expand to 1 , 2 , 3 , 4

foo //the second time this will expand to just 1

Эта опция должна сделать автоматическую генерацию кода честнойнемного проще, хотя, к сожалению, только на gcc (может быть, clang, не проверял)

Если честно, я не могу найти причину, почему это должно работать, скорее всего, это неопределенное поведение, которое работает.Я предполагаю, что причина в том, что после выталкивания foo текущий раскрываемый макрос больше не связан с именем foo, которое позволяет расширять символ foo, но это только моя гипотеза

Редактировать:

После тестирования на clang этот не работает на clang.

Я не знаю, почему я думал, что clang не работает, возможно, он не работалдругая машина.Я определенно заставил его работать с приведенным кодом, хотя

2 голосов
/ 29 декабря 2010

Я не уверен, поможет ли это, но вы можете делать разные макросы. Мистер Конрад из проекта x264 любит злоупотребление препроцессором. Если они звучат так, как будто могут помочь, вы можете узнать больше Здесь

...