Come mappare facilmente enumerazioni c ++ su stringhe


119

Ho un sacco di tipi di enum in alcuni file di intestazione della libreria che sto usando e voglio avere un modo per convertire i valori enum in stringhe utente - e viceversa.

RTTI non lo farà per me, perché le "stringhe utente" devono essere un po 'più leggibili delle enumerazioni.

Una soluzione di forza bruta sarebbe un insieme di funzioni come questa, ma penso che sia un po 'troppo C-like.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

Ho la sensazione viscerale che esista una soluzione elegante utilizzando i modelli, ma non riesco ancora a capirlo.

AGGIORNAMENTO: Grazie per i suggerimenti - Avrei dovuto chiarire che le enumerazioni sono definite nell'intestazione di una libreria di terze parti, quindi non voglio doverne modificare la definizione.

La mia sensazione istintiva ora è di evitare i modelli e fare qualcosa del genere:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);

Risposte:


60

Se vuoi che i nomi enum stessi come stringhe, vedi questo post . Altrimenti, a std::map<MyEnum, char const*>funzionerà bene. (Non ha senso copiare i valori letterali della stringa in std :: strings nella mappa)

Per ulteriore zucchero sintattico, ecco come scrivere una classe map_init. L'obiettivo è permettere

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

La funzione template <typename T> map_init(T&)restituisce un file map_init_helper<T>. map_init_helper<T>memorizza un T & e definisce il banale map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Il ritorno *thisda operator()consente il concatenamento di operator(), come operator<<su std::ostreams)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Poiché la funzione e la classe helper sono basate su modelli, è possibile utilizzarle per qualsiasi mappa o struttura simile a una mappa. Vale a dire che può anche aggiungere voci astd::unordered_map

Se non ti piace scrivere questi helper, boost :: assign offre la stessa funzionalità pronta all'uso.


Hai ragione a fare riferimento a un'altra domanda. Le persone dovrebbero dare un'occhiata alle "domande correlate" prima di postare ...
xtofl

2
@xtofl: Le "domande correlate" mostrate qui sono totalmente diverse dalle domande correlate elencate quando ho pubblicato la domanda!
Roddy

@MSalters, uno std :: map è un modo utile per gestire l'implementazione, ma sto cercando alcuni modi per ridurre il codice boilerplate che potrebbe richiedere.
Roddy

@MSalters, sarebbe bello poter accettare più argomenti per l'operatore []. ma purtroppo non si può farlo. x [a, b] restituisce x [b]. l'espressione (a, b) utilizza l'operatore virgola. quindi è equivalente a ["A"] ["B"] ["C"] nel codice. potresti cambiarlo per dire [eValue1] ["A"] [eValu ..
Johannes Schaub - litb

anche l'operatore di chiamata di funzione sarebbe un buon candidato: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... allora è equivalente a boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb

31

La soluzione MSalters è buona ma fondamentalmente reimplementa boost::assign::map_list_of. Se hai boost, puoi usarlo direttamente:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}

Come lo useresti dove eeeToString è un membro dati di una classe? Ricevo "Errore: l'inizializzazione del membro dati non è consentita"
Utente

@User: i membri dei dati di classe vengono inizializzati nei costruttori, in genere nell'elenco degli inizializzatori.
MSalters

C'è un modo per farlo funzionare per tutte le enumerazioni. Ho più dichiarazioni enum e non voglio che la mappa funzioni solo per il tipo eeenel tuo caso.
Justin Liang

Ho provato ad utilizzare un modello, ma poi ottenuto e l'errore: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang

4
In realtà questa risposta è in gran parte obsoleta con C ++ 11.
Alastair,

19

Genera automaticamente un modulo da un altro.

Fonte:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

generated:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Se i valori enum sono grandi, un form generato potrebbe utilizzare unordered_map <> o modelli come suggerito da Constantin.

Fonte:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

generated:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Esempio:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}

Sebbene sia più veloce, non è facile come @MSalters.
kenny

2
È se hai un po 'di perl / python per leggere un elenco di stringhe da un file di testo e generare un file .h con il carattere statico in fase di compilazione. = "Scrivi programmi per scrivere programmi"
Martin Beckett,

@mgb: perl / python non sono le uniche opzioni che quasi tutti i template engine in qualsiasi linguaggio (in questo caso si stanno generando entrambi i form da un template).
jfs

@jf. Sì, il punto importante era costruire automaticamente tabelle di dati statici in fase di compilazione. Probabilmente preferirei semplicemente generare un array statico stupido.
Martin Beckett

Funzionerà se lo stato non è noto in fase di compilazione? Sono abbastanza sicuro che non lo farà - in teoria il compilatore dovrebbe istanziare il modello enum2str con tutti i possibili valori di enum, cosa che sono abbastanza sicuro che gcc (almeno) non farà.
Alastair

11

Ricordo di aver risposto altrove su StackOverflow. Ripetendolo qui. Fondamentalmente è una soluzione basata su macro variadiche ed è abbastanza facile da usare:

#define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \
inline std::ostream& operator<<(std::ostream& os, name value) { \
std::string enumName = #name; \
std::string str = #__VA_ARGS__; \
int len = str.length(); \
std::vector<std::string> strings; \
std::ostringstream temp; \
for(int i = 0; i < len; i ++) { \
if(isspace(str[i])) continue; \
        else if(str[i] == ',') { \
        strings.push_back(temp.str()); \
        temp.str(std::string());\
        } \
        else temp<< str[i]; \
} \
strings.push_back(temp.str()); \
os << enumName << "::" << strings[static_cast<int>(value)]; \
return os;} 

Per usarlo nel tuo codice, fai semplicemente:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;

1
Basta cambiare la dichiarazione della classe enum in enum per lavorare su pre c ++ 11.
Debdatta Basu

1
Hai ragione, funziona (anche l'auto è solo c ++ 11). Bella soluzione! Sarebbe perfetto se potessi anche impostare un valore per alcune enumerazioni
jamk

Immagino di aver visto in boost qualcosa del genere
Sergei

10

Suggerisco un mix di utilizzo di X-macro sono la soluzione migliore e le seguenti funzioni del modello:

Per prendere in prestito fuori marcinkoziukmyopenidcom ed esteso

enum Colours {
#   define X(a) a,
#   include "colours.def"
#   undef X
    ColoursCount
};

char const* const colours_str[] = {
#   define X(a) #a,
#   include "colours.def"
#   undef X
    0
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)

C'è un modo per rendere generica la definizione dell'array di stringhe enum? (Non so come gestire una X-Macro all'interno di una macro e non gestisco facilmente il modello)
Jonathan

5

Uso questa soluzione che riproduco di seguito:

#define MACROSTR(k) #k

#define X_NUMBERS \
       X(kZero  ) \
       X(kOne   ) \
       X(kTwo   ) \
       X(kThree ) \
       X(kFour  ) \
       X(kMax   )

enum {
#define X(Enum)       Enum,
    X_NUMBERS
#undef X
} kConst;

static char *kConstStr[] = {
#define X(String) MACROSTR(String),
    X_NUMBERS
#undef X
};

int main(void)
{
    int k;
    printf("Hello World!\n\n");

    for (k = 0; k < kMax; k++)
    {
        printf("%s\n", kConstStr[k]);
    }

    return 0;
}

1
Si tratta di macro X di base e sono sbalordito dal fatto che questa sia la prima risposta qui a suggerirlo! +1
Gare di leggerezza in orbita

4

Se vuoi ottenere rappresentazioni di stringa di MyEnum variabili , i modelli non lo taglieranno. Il modello può essere specializzato su valori integrali noti in fase di compilazione.

Tuttavia, se è quello che vuoi, prova:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

Questo è dettagliato, ma rileverà errori come quello che hai fatto in questione: il tuo case VAL1è duplicato.


In realtà il nome del metodo () non è necessario. Vedi la mia risposta.
jfs

3

Ho passato più tempo a fare ricerche su questo argomento che vorrei ammettere. Fortunatamente esistono ottime soluzioni open source in circolazione.

Questi sono due ottimi approcci, anche se non abbastanza conosciuti (ancora),

wise_enum

  • Libreria standalone smart enum per C ++ 11/14/17. Supporta tutte le funzionalità standard che ti aspetteresti da una classe enum intelligente in C ++.
  • Limitazioni: richiede almeno C ++ 11.

Enumerazioni migliori

  • Libreria enum in fase di compilazione riflettente con sintassi pulita, in un unico file di intestazione e senza dipendenze.
  • Limitazioni: basato su macro, non può essere utilizzato all'interno di una classe.

2

Sarei tentato di avere una mappa m e di incorporarla nell'enumerazione.

configurazione con m [MyEnum.VAL1] = "Value 1";

e tutto è fatto.


2

Ho richiesto questa funzionalità più volte per eseguire il debug / analizzare il codice da altri. Per questo, ho scritto uno script Perl che genera una classe con diversi toStringmetodi sovraccaricati . Ogni toStringmetodo accetta un Enumcome argomento e restituisce const char*.

Ovviamente, lo script non analizza C ++ per le enumerazioni stesse, ma utilizza i tag ctags per generare la tabella dei simboli.

Lo script Perl è qui: http://heinitz-it.de/download/enum2string/enum2string.pl.html


2

Le tue risposte mi hanno ispirato a scrivere alcune macro da solo. I miei requisiti erano i seguenti:

  1. scrivi ogni valore dell'enumerazione una sola volta, quindi non ci sono doppi elenchi da mantenere

  2. non mantenere i valori enum in un file separato che viene successivamente #incluso, così posso scriverlo dove voglio

  3. non sostituire l'enumerazione stessa, voglio comunque avere il tipo di enum definito, ma in aggiunta ad esso voglio essere in grado di mappare ogni nome enum sulla stringa corrispondente (per non influenzare il codice legacy)

  4. la ricerca dovrebbe essere veloce, quindi preferibilmente senza switch-case, per quegli enormi enumeratori

Questo codice crea un'enumerazione classica con alcuni valori. Inoltre crea come std :: map che mappa ogni valore di enum al suo nome (cioè map [E_SUNDAY] = "E_SUNDAY", ecc.)

Ok, ecco il codice ora:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // questo è il file che vuoi includere ogni volta che devi fare queste cose, userai le macro da esso:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // questo è un esempio di come utilizzarlo per creare un'enumerazione personalizzata:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

Saluti.


2

Ecco un tentativo di ottenere gli operatori di flusso << e >> su enum automaticamente con un solo comando macro di una riga ...

definizioni:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Uso:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Non sono sicuro dei limiti di questo schema però ... i commenti sono benvenuti!


1

nell'intestazione:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

nel file .cpp:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Avvertenza: non gestire un indice di matrice errato. :) Ma puoi facilmente aggiungere una funzione per verificare l'enumerazione prima di ottenere la stringa dall'array.


Davvero una soluzione molto non DRY-SPOT.
xtofl

ora che parli di DRY. i file .he .cpp generati automaticamente da un altro file di input. Mi piacerebbe vedere soluzioni migliori (che non ricorrono a complessità inutili)
Moogs

1

Volevo solo mostrare questa possibile soluzione elegante usando le macro. Questo non risolve il problema, ma penso che sia un buon modo per ripercorrere il problema.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- MODIFICARE ----

Dopo alcune ricerche su Internet e alcune esperienze personali sono arrivato alla seguente soluzione:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Volevo solo postarlo forse qualcuno potrebbe trovare utile questa soluzione. Non c'è bisogno di classi template, non c'è bisogno di c ++ 11 e non c'è bisogno di boost, quindi questo potrebbe essere usato anche per semplici C.

---- EDIT2 ----

la tabella delle informazioni può produrre alcuni problemi quando si utilizzano più di 2 enumerazioni (problema del compilatore). La soluzione alternativa seguente ha funzionato:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};

1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Sopra è la mia semplice soluzione. Un vantaggio è il "NUM" che controlla la dimensione dell'array di messaggi, impedisce anche l'accesso fuori confine (se lo usi con saggezza).

Puoi anche definire una funzione per ottenere la stringa:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Oltre alla mia soluzione, ho trovato la seguente piuttosto interessante. In genere ha risolto il problema di sincronizzazione di quello precedente.

Diapositive qui: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Codice qui: https://github.com/arunksaha/enum_to_string


1

So di essere in ritardo per festeggiare, ma per tutti gli altri che vengono a visitare questa pagina, potresti provarlo, è più facile di tutto lì e ha più senso:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Stai suggerendo di usare le stringhe e di non usare affatto le enumerazioni? Questo non risolve davvero il problema.
Roddy

0

Recentemente ho avuto lo stesso problema con una libreria di fornitori (Fincad). Fortunatamente, il fornitore ha fornito la documentazione xml per tutte le enumerazioni. Ho finito per generare una mappa per ogni tipo di enum e fornire una funzione di ricerca per ogni enum. Questa tecnica consente anche di intercettare una ricerca al di fuori dell'intervallo dell'enumerazione.

Sono sicuro che swig potrebbe fare qualcosa di simile per te, ma sono felice di fornire le utilità di generazione del codice scritte in ruby.

Ecco un esempio del codice:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

Sembra che tu voglia andare dall'altra parte (da enum a stringa, piuttosto che da stringa a enum), ma dovrebbe essere banale da invertire.

-Whit


1
a) Qualcun altro lo trova assolutamente illeggibile? Alcuni typedef e l'utilizzo di dichiarazioni migliorerebbero notevolmente la leggibilità. b) le dichiarazioni statiche locali non sono threadsafe. c) usa const string e invece di char *, d) che ne dici di includere il valore che non è stato trovato nell'eccezione generata?
Alastair

0

Verifica se la seguente sintassi ti si addice:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

In tal caso, potresti consultare questo articolo:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

questo vecchio pasticcio è il mio sforzo basato su pezzi e pezzi di SO. Il for_each dovrebbe essere espanso per supportare più di 20 valori enum. Testato su visual studio 2019, clang e gcc. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

che produce il codice seguente

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Peccato che i cerchi che devi saltare con il preprocessore per farlo in uno dei linguaggi di programmazione più usati al mondo ...


0

Utilizzando gli inizializzatori di array designati, l'array di stringhe è indipendente dall'ordine degli elementi nell'enumerazione:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.