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

Potrzebuję zdefiniować MACRO() w taki sposób, że powyższy kod wygeneruje następujący kod.C programowanie, zmiana w dwóch miejscach z jednym makro

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

Czy to możliwe, czy też alternatywa dla uzyskania tego, co próbuję zrobić?

+2

Możesz być w stanie to osiągnąć, ale to będzie tak zawiłe, że nie polecam go. Jaki jest twój większy cel? Dlaczego tego chcesz? Wygląda na [Problem XY] (http://meta.stackexchange.com/a/66378). – tenfour

+0

Uważam, że to, co próbujesz zrobić, nie może zostać osiągnięte za pomocą makr. – Codor

+1

Użyj [XMacro] (http://en.wikipedia.org/wiki/X_Macro) –

Odpowiedz

1

Co chcesz, to mieć jedną listę, która automatycznie wygeneruje definicję i listę nazw, prawda?

Jeśli tak, wyszukaj X Macros w google.

Przykład:

#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

Nie można to zrobić bezpośrednio, ale można po przeniesieniu makr do oddzielnego miejsca (takie jak osobnym pliku):

macros.hpp

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

#undef MACRO 

się inny plik

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" 
    } 
}; 

Alternatywnie, można osiągnąć podobny efekt z Boost.Preprocessor.

+0

Jest to zazwyczaj właściwe rozwiązanie. Jeśli numer jest używany do wyszukiwania nazwy, użyłbym Enum do autogeneracji poprawnego indeksu tablicy: '#define MACRO (a, b) b ## _ Offset,' i 'enum {#include" macros.hpp "} ; 'W ten sposób indeks tablicy jest programowo niezależny od liczby używanych makr. 'if (błąd == Połącz) log_error (name [Connect_Offset]);' Ale może nie potrzebujesz tego? – Speed8ump

2

Oto Boost.Preprocessor rozwiązanie:

#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; 
} 

Musisz upewnić się podwójnym uchwytem każda ((Token, wartość)) Para jednak nie potrzebujemy osobny plik dla twojego makra.

0

Wygląda na to, że próbujesz zdefiniować enum Error, który również ma łańcuchy jako członków. Dam ci własne rozwiązanie tego problemu. (Nie odpowiadam na to pytanie, ale uważam, że moja odpowiedź jest istotna dla tego, co rozumiem, że OP próbuje zrobić.)

I właśnie zdałem sobie sprawę, że OP celuje w C, nie w C++, więc nie jestem pewien, czy to można to zrobić ...

w 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];}        \ 
    } 

Tutaj findEnum() tylko przeszukiwanie liniowe nad wektorem, który zwraca indeks pozycji (dodatkowo, w mojej realizacji, jeśli nie znajdzie to zgłasza wyjątek z wszystkimi możliwymi poprawnymi danymi wejściowymi, ja też nie rozróżniam wielkości liter). Zauważ, że uporządkowana mapa zamiast wektora byłaby bardziej wydajna (O (log (n)) zamiast O (n)), ale nie obchodziło mnie to zbytnio, ponieważ rozmiar tych rzeczy jest bardzo mały w moim przypadku.

Poniżej poprzedniego makro, zadeklarować enum jako

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

I MyEnum.cpp, dodać

#include <boost/assign/list_of.hpp> 

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

(myślę, że powinno być możliwe użycie list inicjalizacji z nowoczesnym kompilatora).Ważne jest, aby upewnić się, że zamówienie jest takie samo, w przeciwnym razie nie będzie działać.

Następnie można zrobić rzeczy jak to:

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