2014-04-17 6 views
5
struct Error 
{ 
    MACRO(1, Connect); 
    MACRO(2, Timeout); 
};  

Devo definire MACRO() in modo tale che il codice precedente generi il seguente codice.Programmazione C, apportare modifiche in due posizioni con una macro

struct Error 
{ 
    static const int Connect = 1; 
    static const int Timeout = 2; 
    const char * const name[] = {"Connect", "Timeout"}; 
}; 

È possibile o qual è l'alternativa per ottenere ciò che sto cercando di fare?

+2

potrebbe essere in grado di raggiungere questo obiettivo, ma sarà così contorto io non lo consiglio. Qual è il tuo obiettivo più grande? Perché lo vuoi? Sembra un [XY Problem] (http://meta.stackexchange.com/a/66378). – tenfour

+0

Credo che ciò che si sta tentando di fare non possa essere raggiunto con i macro. – Codor

+1

Utilizzare [XMacro] (http://en.wikipedia.org/wiki/X_Macro) –

risposta

1

Quello che vuoi è avere un'unica lista, che genererà automaticamente la definizione e la lista dei nomi, corretta?

In tal caso, cercare X Macro in google.

Esempio:

#define EXPAND_AS_DEFINITION(a, b) static const int b = a; 
#define EXPAND_AS_ARRAY(a, b) #b, 

#define STATE_TABLE(ENTRY) \ 
    ENTRY(1, Connect)  \ 
ENTRY(2, Timeout) 

struct Error 
{  
    STATE_TABLE(EXPAND_AS_DEFINITION) 
    static const char * const name[];  
}; 

const char * const Error::name[] = {STATE_TABLE(EXPAND_AS_ARRAY) 0}; 
2

Non puoi farlo direttamente, ma è possibile se si spostare le macro in una posizione separata (come ad esempio un file separato):

macros.hpp

MACRO(1, Connect) 
MACRO(2, Timeout) 

#undef MACRO 

la altro file

struct Error 
{ 
    #define MACRO(a, b) static const int b = a; 
    #include "macros.hpp" 

    const char * const name [] = { 
    #define MACRO(a, b) #b, 
    #include "macros.hpp" 
    } 
}; 

In alternativa, si potrebbe ottenere un effetto simile con Boost.Preprocessor.

+0

Questa è generalmente la soluzione giusta. Se il numero è utilizzato per cercare il nome, utilizzerei un Enum per generare automaticamente un indice di array corretto: '#define MACRO (a, b) b ## _ Offset,' e 'enum {#include" macros.hpp "} ; 'In questo modo l'indice dell'array è programmaticamente indipendente dal numero di macro utilizzate. 'if (error == Connect) log_error (name [Connect_Offset]);' Ma forse non ne hai bisogno? – Speed8ump

2

Ecco una soluzione Boost.Preprocessor:

#include <boost/preprocessor/seq/for_each.hpp> 
#include <boost/preprocessor/seq/size.hpp> 
#include <boost/preprocessor/tuple/elem.hpp> 
#include <boost/preprocessor/stringize.hpp> 

#define FIRST(a, b) a 
#define SECOND(a, b) b 

#define DECLARE_VAR(r, data, elem)    \ 
    static const int FIRST elem = SECOND elem; 

#define NAME_ARRAY_ELEM(r, data, elem)   \ 
    BOOST_PP_STRINGIZE(FIRST elem), 

#define MACRO(seq)          \ 
    BOOST_PP_SEQ_FOR_EACH(DECLARE_VAR, ~, seq)   \ 
    const char * const name[] = {      \ 
     BOOST_PP_SEQ_FOR_EACH(NAME_ARRAY_ELEM, ~, seq) \ 
    } 

int main() 
{ 
    MACRO(((Connect, 1))((TimeOut, 2))); 
    return 0; 
} 

Bisogna fare in modo di doppia staffa ciascuna (Token, il valore)() coppia, tuttavia non è necessario un file separato per la tua macro.

0

Sembra che si stia tentando di definire uno enum Error che abbia anche le stringhe come membri. Ti darò la mia soluzione a questo problema. (Non sto affrontando la domanda ma credo che la mia risposta sia rilevante per quello che capisco che OP sta cercando di fare.)

E ho appena realizzato che l'OP ha come target C, non C++, quindi non so se questo si può fare ...

in MyEnum.hpp

#define MYENUM(X,...)          \ 
    struct X {            \ 
     enum Enum {__VA_ARGS__};        \ 
     static const std::vector<std::string> names;   \ 
     static X::Enum which(const std::string& s) {   \ 
      return static_cast<X::Enum>(findEnum(s,names)); \ 
     }             \ 
     static std::string str(X::Enum i) {     \ 
      return names[i];}        \ 
    } 

Qui findEnum() è solo una ricerca lineare sopra il vettore che restituisce l'indice di posizione (in aggiunta, nella mia implementazione se non lo trova viene generata eccezione con tutti gli input corretti possibili, faccio anche il confronto case sensitive). Si noti che una mappa ordinata invece di un vettore sarebbe più efficiente (O (log (n)) invece di O (n)), ma non mi è importato molto perché la dimensione di queste cose è molto piccola nel mio caso.

Sotto la macro precedente, dichiara il tuo enum come

MYENUM(Error,Connect,Timeout); // I put the semicolon here not in the macro 

E in MyEnum.cpp, aggiungere

#include <boost/assign/list_of.hpp> 

const std::vector<std::string> Error::names = boost::assign::list_of 
("Connect")("Timeout"); 

(penso che dovrebbe essere possibile utilizzare gli elenchi di inizializzazione con un compilatore moderno).La cosa importante qui è assicurarsi che l'ordine sia lo stesso, altrimenti non funzionerà.

Poi, si può fare cose come questa:

Error::Enum err1 = Error::Connect; 
Error::Enum err2 = Error::which("Timeout"); 
std::cout << "Got " << Error::str(err1) << " error. Not good.\n"; 
Problemi correlati