Могу ли я добавить к макросу препроцессора?
Есть ли способ в стандартном 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-макроса работать с этой библиотекой.
3 ответа
Фактически нет.
Макросы лениво оцениваются. Когда ты #define List_ Expand(List)
, его список замен представляет собой последовательность из четырех токенов Expand
, (
, List
, а также )
, Нет никакого способа расширить макрос в список замены.
Замена всех макросов происходит при вызове макроса.
Я бы рекомендовал использовать библиотеку Boost.Preprocessor для автоматической генерации кода. Это немного работы, но вы можете сделать некоторые довольно впечатляющие вещи, используя его. Это должно быть полностью совместимо с C.
Есть выход!
Используя новое ключевое слово _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 не работает, может быть, это не на другой машине. Я определенно заставил его работать с приведенным кодом, хотя
Я не уверен, поможет ли это, но вы можете делать различные макросы. Мистер Конрад из проекта x264 любит злоупотребление препроцессором. Если они звучат так, как будто они могут помочь, вы можете узнать больше здесь