2017-04-12 88 views
9

Próbuję zastosować koncepcję X Macro, aby mieć możliwość inicjowania wszystkich członków struktury do niestandardowej wartości domyślnej (nieprawidłowej). I napisać następujący kod:Dlaczego ta zagnieżdżona wymiana makr nie działa?

#define LIST_OF_STRUCT_MEMBERS_foo \ 
    X(a) \ 
    X(b) \ 
    X(c) 

#define X(name) int name; 
struct foo { 
    LIST_OF_STRUCT_MEMBERS_foo 
}; 
#undef X 


#define X(name) -1, 
static inline void foo_invalidate(struct foo* in) { 
    *in = (struct foo){ 
    LIST_OF_STRUCT_MEMBERS_foo 
    }; 
} 
#undef X 

#define X(name) -1, 
#define foo_DEFAULT_VALUE { LIST_OF_STRUCT_MEMBERS_foo } 
#undef X 

static struct foo test = foo_DEFAULT_VALUE; 

Jednak kiedy uruchomić preprocesora, definicja foo_DEFAULT_VALUE nie zastępować połączenia X(name) z wyjściem -1,

Preprocessor:

struct foo { 
    int a; int b; int c; 
}; 

static inline void foo_invalidate(struct foo* in) { 
    *in = (struct foo){ 
    -1, -1, -1, /*Here the substitution worked nicely*/ 
    }; 
} 

static struct foo test = { X(a) X(b) X(c) }; /*Why this substitution failed?*/ 

myślałem C-macros could refer to other macros . Czy wiesz, dlaczego ta substytucja zawodzi? Czy istnieje jakieś obejście?

Mogłem żyć z foo_invalidate, ale nie chcę zrezygnować z wartości, która ma być użyta bezpośrednio przy inicjalizacji.

+2

Masz 'X (name)' zdefiniowany jako '-1,' wokół '# define' dla' foo_DEFAULT_VALUE', ale nie tam, gdzie faktycznie * użyłeś * go. Potrzebujesz makra 'X' zdefiniowanego tam, gdzie występuje podstawienie, wokół linii' static struct foo test = foo_DEFAULT_VALUE; '. – Dmitri

Odpowiedz

9

Udawajmy że jesteśmy preprocesor i napotykając linię:

static struct foo test = foo_DEFAULT_VALUE; 

karnet 1:

static struct foo test = { LIST_OF_STRUCT_MEMBERS_foo }; 

Przebieg 2:

static struct foo test = { X(a) X(b) X(c) }; 

karnetów 3: Nic rozwinąć jako X jest niezdefiniowany w tym wierszu.


Jeden obejście może być zdefiniowanie const zmiennej (być może, ale niekoniecznie static), który będzie używany jako wartość domyślna:

#define X(name) -1, 
static const struct foo foo_DEFAULT_VALUE = { LIST_OF_STRUCT_MEMBERS_foo }; 
#undef X 

który generuje:

static const struct foo foo_DEFAULT_VALUE = { -1, -1, -1, }; 
+0

Co może być obejściem problemu? Myślałem o następujących, ale zastanawiam się, czy jest optymalny: '#define X (name) -1, #define foo_DEFAULT_VALUE_GENERATOR {LIST_OF_STRUCT_MEMBERS_foo} static const struct foo_DEFAULT_VALUE = foo_DEFAULT_VALUE_GENERATOR; #undef X' – Antonio

+0

Cóż, najprostszym sposobem byłoby pozornie to. Przenieś '# undef' po linii z deklaracją.W przeciwnym razie konieczne byłoby przeprojektowanie. –

+0

Problem polega na tym, że konstrukcja X Makra wymaga niezdefiniowania 'X()' "szybko" ... – Antonio

2

może lubisz bezproblemowa wersja X_Macros,
redukuje niezbędną ostrożność przy definiowaniu i undefiningu przy każdym użyciu
i lepiej nadaje się do definicji w useage nagłówek und w wielu plikach kod:

#define LIST_OF_STRUCT_MEMBERS_foo(mode) \ 
    X_##mode(a) \ 
    X_##mode(b) \ 
    X_##mode(c) 

#define X_struct(name) int name; 
#define X_list(name) -1, 
#define foo_DEFAULT_VALUE { LIST_OF_STRUCT_MEMBERS_foo(list) } 

struct foo { 
    LIST_OF_STRUCT_MEMBERS_foo(struct) 
}; 

static inline void foo_invalidate(struct foo* in) { 
    *in = (struct foo){ 
    LIST_OF_STRUCT_MEMBERS_foo(list) 
    }; 
} 

static struct foo test = foo_DEFAULT_VALUE; 

Output (gcc -E):

struct foo { 
    int a; int b; int c; 
}; 

static inline void foo_invalidate(struct foo* in) { 
    *in = (struct foo){ 
    -1, -1, -1, 
    }; 
} 

static struct foo test = { -1, -1, -1, }; 
+0

Bardzo interesująca metoda !!! Gdzie się o tym dowiedziałeś? – Antonio

+1

Uwierzysz mi, jeśli powiem, że to wymyśliłem? Nie chodzi mi o to, że go wymyśliłem * po pierwsze * (mogli być inni, wcześniej), ale wymyśliłem to sam. – Yunnosch

+0

Sądzę, że bardzo wczoraj przeszukałem temat i nigdy nie spotkałem się z takim wdrożeniem! Przyznam ci nagrodę tak szybko, jak to tylko możliwe! – Antonio

-1

Można także spróbować sprawdzić wyjście rozbudowanych makr . Jeśli używasz gcc jako kompilatora, pomoże ci gcc -E <filename> >> full_src.txt'. Więcej szczegółów można znaleźć tutaj: Seeing expanded C macros

+0

Dokładnie to zrobiłem, aby wypełnić sekcję 'Preprocessor output:' mojego pytania – Antonio