2010-12-28 3 views
9

Есть ли какой-либо способ в стандартном 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 работать с этой библиотекой.

+0

Да, это определенно выполнимо, если вы не настаиваете на переопределении. Несколько больно, если вы используете исходный препроцессор. Несколько менее болезненным с Boost.Preprocessor. Увы, мой холод мешал мне думать достаточно прямо, чтобы создать метод. Надеюсь, кто-то еще это сделает. – swestrup

ответ

5

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

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

Вся макрозамена выполняется при вызове макроса.

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

+0

Спасибо за объяснение. –

2

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

+1

C99 variadic macro are nice (и я использовал их), но это не имеет отношения к моему вопросу. (Не прямо, по крайней мере, хотя я могу себе представить, что фактическое решение может где-то _use_ variadic macros.) –

2

Существует способ!

Использования нового _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, не были протестированы)

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

Смежные вопросы