2015-09-25 10 views
5

Sto tentando di semplificare la dichiarazione di un array, ma ho riscontrato un problema con i preprocessori che sto utilizzando. Il mio codice iniziale sia simile alla seguente:Il preprocessore non riesce a causa di - '#' non è seguito da un parametro macro

#define REQ_ENTRY(parm_1, parm_2) \ 
#if defined(parm_1)     \ 
    { parm_1, parm_2 },    \ 
#endif 

typedef struct { 
    int parm1, 
     parm2; 
} MyTypedef_t; 

static const MyTypedef_t MyList[] = 
{ 
    REQ_ENTRY(ID_1, 1) 
    REQ_ENTRY(ID_2, 2) 
    REQ_ENTRY(ID_3, 3) 
    REQ_ENTRY(ID_4, 4) 
    REQ_ENTRY(ID_5, 5) 
}; 

La compilazione fallisce, ovviamente, con il messaggio di errore di "errore: '#' non è seguito da un parametro di macro". La ragione di questo è spiegato qui (Why compiler complain about this macro declaration)

Fondamentalmente, sto cercando di evitare dichiara la matrice come segue, che funziona:

static const MyTypedef_t MyList[] = 
{ 
    #if defined (ID_1) 
    { ID_1, 1 }, 
    #endif 

    #if defined (ID_2) 
    { ID_2, 2 }, 
    #endif 

    #if defined (ID_3) 
    { ID_3, 3 }, 
    #endif 

    #if defined (ID_4) 
    { ID_4, 4 }, 
    #endif 

    #if defined (ID_5) 
    { ID_5, 5 }, 
    #endif   
}; 

l'elenco può essere piuttosto lungo, variabili a seconda della costruire il tipo del progetto. Ho provato a pensare di usare x-macros, ma penso che avrei lo stesso problema. Spero che qualcuno possa vedere un modo per creare i macro del preprocessore in modo tale da ottenere la sintassi dello zucchero originale? Qualsiasi consiglio é ben accetto.

+6

Puoi racchiudere '# ifdef' attorno a una definizione di macro; non puoi incorporarlo all'interno di uno. Inoltre, non è possibile testare direttamente se un argomento di una macro è una macro definita.Sfortunatamente, il nay-say è la parte facile; non è chiaro che ci sia un buon modo per evitare il codice originale. Questo è quello che ho usato in circostanze equivalenti. –

risposta

3

Non c'è una bella soluzione pulita. Ma ci sono soluzioni di varia bruttezza.

Se non ti dispiace che comprende sia il id e la sequenzanella definizione di macro, può essere risolto in questo modo:

#define CONCAT2(x,y) x##y 
#define CONCAT(x,y) CONCAT2(x,y) 
#define REQ_ENTRY_YES(p1, p2) { p1 , p2 } 
#define REQ_ENTRY_NO(p1) 
#define IS_PAIR_HELPER(a, b, c, ...) c 
#define IS_PAIR(...) IS_PAIR_HELPER(__VA_ARGS__, YES, NO) 
#define REQ_ENTRY(pair) CONCAT(REQ_ENTRY_, IS_PAIR(pair))(pair) 

#define ID_1 78723649, 1 
#define ID_3 2347602, 3 

typedef struct { 
    int parm1, 
     parm2; 
} MyTypedef_t; 

static const MyTypedef_t MyList[] = 
{ 
    REQ_ENTRY(ID_1) 
    REQ_ENTRY(ID_2) 
    REQ_ENTRY(ID_3) 
    REQ_ENTRY(ID_4) 
    REQ_ENTRY(ID_5) 
}; 

Corri attraverso gcc con -std=c11 -Wall -E, e mostrando solo la MyList definizione:

static const MyTypedef_t MyList[] = 
{ 
    { 78723649 , 1 } 

    { 2347602 , 3 } 


}; 

È possibile fare la stessa cosa utilizzando qualsiasi valore secondo nei macro #define ID_x, purché ce ne sia uno; i parametri reali possono essere aggiunti a REQ_ENTRY. Ma ci vuole un po 'di giocoleria in più.

1

È un peccato che l'operatore defined sia disponibile solo nel contesto di #if e #ifelse, ma non per le espansioni di macro. Così com'è, sono d'accordo con i rici sulle soluzioni di varia bruttezza.

Ecco una soluzione che richiede che i valori definiti siano racchiusi tra parentesi. Puoi quindi utilizzare l'ID come valore normale e puoi anche passarlo a DEF, che si espande a 1 quando la macro è tra parentesi o 0 in caso contrario. (Questo è un trucco che ho imparato here.)

Con l'aiuto del DEF macro, è possibile creare macro ausiliari che si espandono o ignorare la definizione data:

/* Auxiliary macros */ 

#define M_CHECK(...) M_CHECK_(__VA_ARGS__) 
#define M_CHECK_(a, b, ...) b 

#define M_IS_PAREN(x) M_CHECK(M_IS_PAREN_ x, 0) 
#define M_IS_PAREN_(...) 1, 1 

#define M_CONCAT(a, b) M_CONCAT_(a, b) 
#define M_CONCAT_(a, b) a ## b 

/* Conditional definition macros */ 

#define DEF(x) M_IS_PAREN(x) 

#define DEF_IF_0(id, def) 
#define DEF_IF_1(id, def) {id, def}, 

#define COND_DEF(x, y) M_CONCAT(DEF_IF_, DEF(x))(x, y) 

/* Implementation */ 

#define ID_1 (27) 
#define ID_3 (28) 
#define ID_4 (29) 

static const MyTypedef_t MyList[] = { 
    COND_DEF(ID_1, 1) 
    COND_DEF(ID_2, 2) 
    COND_DEF(ID_3, 3) 
    COND_DEF(ID_4, 4) 
    COND_DEF(ID_5, 5) 
}; 

Questo produrrà:

static const MyTypedef_t MyList[] = { 
    {(27), 1}, 

    {(28), 3}, 
    {(29), 4}, 

}; 

È inoltre possibile utilizzare la macro DEF nel codice, che sarà ampliato a 0 o 1:

printf("ID_1 is %s.\n", DEF(ID_1) ? "defined" : "undefined"); 
Problemi correlati