2009-04-07 17 views
17

C'è un modo per associare una stringa da un file di testo con un valore enum?Stringa per enum in C++

Il problema è: ho alcuni valori di enumerazione memorizzati come stringa in un file di testo che ho letto al volo incontrando alcune condizioni ... Ora voglio assegnare il valore letto a un enum.

Qual è il modo più efficace per farlo? Non ha bisogno di essere l'approccio più semplice. .

risposta

31

È possibile impostare una mappa che è possibile utilizzare più e più volte:

template <typename T> 
class EnumParser 
{ 
    map <string, T> enumMap; 
public: 
    EnumParser(){}; 

    T ParseSomeEnum(const string &value) 
    { 
     map <string, T>::const_iterator iValue = enumMap.find(value); 
     if (iValue == enumMap.end()) 
      throw runtime_error(""); 
     return iValue->second; 
    } 
}; 

enum SomeEnum 
{ 
    Value1, 
    Value2 
}; 
EnumParser<SomeEnum>::EnumParser() 
{ 
    enumMap["Value1"] = Value1; 
    enumMap["Value2"] = Value2; 
} 

enum OtherEnum 
{ 
    Value3, 
    Value4 
}; 
EnumParser<OtherEnum>::EnumParser() 
{ 
    enumMap["Value3"] = Value3; 
    enumMap["Value4"] = Value4; 
} 

int main() 
{ 
    EnumParser<SomeEnum> parser; 
    cout << parser.ParseSomeEnum("Value2"); 
} 
+0

Perché non utilizzare un hasmap? Renderebbe la ricerca più efficiente? Beh, deve provare e misurare l'efficacia in quanto può dipendere dal numero di elementi nel tuo enum ... Con enum grande, hasmap potrebbe renderlo un modo più veloce per ottenere il valore, non è vero? –

+1

hash_map non fa parte dello standard (e unordered_map è stato aggiunto solo in TR1). Inoltre, è probabile che ci sia una minima differenza tra hash_map e map a meno che non si inizi ad avere un numero molto elevato di valori enum.In ogni caso, è un passaggio facile da fare se la profilatura indica che qualcosa è troppo lento. La mappa di hash – Eclipse

+1

sarebbe molto inefficiente per la maggior parte delle enumerazioni che hanno un numero molto limitato di valori. In effetti, la ricerca lineare può essere più efficiente di una mappa hash se l'enumerazione riguarda solo i normali 8 valori. Se ha 2 o 3, la ricerca lineare probabilmente batterà anche std :: map. – CashCow

9
std::map< string, enumType> enumResolver; 
0

analizzare la stringa da soli, hanno la stringa con un valore (che è anche un indice a un map<string, enum>

2

Utilizzando un std::map solleva la questione:? come fa la mappa ottiene inizializzato avrei preferito utilizzare una funzione:

enum E { A, B }; 

E f(const std::string & s) { 
    if (s == "A") { 
     return A; 
    } 
    else if (s == "B") { 
     return B; 
    } 
    else { 
     throw "Your exception here"; 
    } 
} 
+1

come sta inizializzando la mappa un problema? –

+0

Devi garantire che venga eseguito prima di utilizzarlo per mappare le stringhe. Questo è possibile, ma solo scrivendo una funzione. –

+0

La mia inclinazione è verso la mappa, dal momento che la ricerca è veloce, ma Neil, la tua risposta mi ha fatto riflettere: le enumerazioni sono conosciute in fase di compilazione, come lo sono le loro rappresentazioni di stringa. Esiste un modo efficiente e scalabile per implementare tutto questo in fase di compilazione? – veefu

3

Sono d'accordo con molte delle risposte che std::map è la soluzione più semplice.

Se hai bisogno di qualcosa di più veloce, puoi usare una mappa di hash. Forse il tuo compilatore ne offre già uno, come ad esempio hash_map o l'imminente standard unordered_map, oppure puoi ottenerne uno da boost. Quando tutte le stringhe sono conosciute in anticipo, è possibile utilizzare anche perfect hashing.

+1

Non sono d'accordo sul fatto che hash_map sarà più veloce in generale, tuttavia se i tuoi nomi sono "corretti" puoi utilizzare alcune proprietà speciali che conosci, ad es. sono tutti unici nella terza lettera o in qualche altra combinazione. Quindi cambia o qualunque cosa sul valore che generi. Se stai elaborando un tipo di feed con un gran numero di valori stringificati che rientrano in un set limitato, questo potrebbe darti dei vantaggi in termini di prestazioni, che è quello che immagino tu intenda per hashing perfetto. – CashCow

+1

@CashCow, non c'è bisogno di indovinare cosa intendo per "hashing perfetto", la frase contiene un link a Wikipedia dove è definito. Sarò d'accordo con te sul fatto che non puoi fare ipotesi sulla velocità e probabilmente dovresti averlo detto nella risposta. –

2

Vedere Boost.Bimap, fornisce associazioni bidirezionali tra due serie di valori. Puoi anche scegliere il contenitore sottostante.

1

E 'questo quello che vuoi? L'inizializzazione è semplice e non è necessaria alcuna istanziazione.

utilizzo:

enum SomeEnum 
{ 
    ENUM_ONE, 
    ENUM_TWO, 
    ENUM_THREE, 
    ENUM_NULL 
}; 

DEFINE_PAIRLIST(CEnumMap, SomeEnum) 

INIT_PAIRLIST(CEnumMap)= 
{ 
     {"One", ENUM_ONE}, 
     {"Two", ENUM_TWO}, 
     {"Three", ENUM_THREE}, 
     {"", ENUM_NULL} 
}; 

main{ 
    // Get enum from string 
    SomeEnum i = CEnumMap::findValue("One"); 

    // Get string from enum 
    SomeEnum eee = ENUM_ONE; 
    const char* pstr = CEnumMap::findKey(eee); 
    ... 
} 

libreria:

template <class T> 
struct CStringPair 
{ 
    const char* _name; 
    T _value; 
}; 

template <class T, class Derived> 
struct CStringPairHandle 
{ 
    typedef CStringPair<T> CPair; 
    static const CStringPair<T> * getPairList(){ 
     return Derived::implementation(); 
    } 
    static T findValue(const char* name){ 
     const CStringPair<T> * p = getPairList(); 
     for (; p->_name[0]!=0; p++) 
      if (strcmp(name,p->_name)==0) 
       break; 
     return p->_value; 
    } 

    static const char* findKey(T value){ 
     const CStringPair<T> * p = getPairList(); 
     for (; p->_name[0]!=0; p++) 
      if (strcmp(value,p->_value)==0) 
       break; 
     return p->_name; 
    }; 
}; 

#define DEFINE_PAIRLIST(name, type) struct name:public CStringPairHandle<type, name>{ \ 
    static CPair _pairList[];  \ 
    static CPair* implementation(){  \ 
     return _pairList;   \ 
    }}; 
#define INIT_PAIRLIST(name) name::CPair name::_pairList[] 
0

risposta accettata non contiene lista completa. Sto aggiungendo EnumParser.h che ho creato dalla risposta accettata, la speranza può aiutare

#include <string> 
#include <map> 

using namespace std; 

template <typename T> class EnumParser 
{ 
    map<string, T> enumMap; 
public: 
    EnumParser(){}; 

    T ParseSomeEnum(const string &value) 
    { 
     typename map <string, T>::const_iterator iValue = enumMap.find(value); 
     if (iValue == enumMap.end()) 
      throw runtime_error(""); 
     return iValue->second; 
    } 
}; 

L'uso è semplice:

enum FieldType 
{ 
    Char, 
    Integer, 
    Long, 
    Fixed, 
    Price, 
    Date, 
    Time 
}; 

EnumParser<FieldType>::EnumParser() 
{ 
    enumMap["Char"] = Char; 
    enumMap["Integer"] = Integer; 
    enumMap["Long"] = Long; 
    enumMap["Fixed"] = Fixed; 
    enumMap["Price"] = Price; 
    enumMap["Date"] = Date; 
    enumMap["Time"] = Time; 
} 

uso:

EnumParser<FieldType> fieldTypeParser; 
FieldType val = fieldTypeParser.ParseSomeEnum(stringValue) 
+0

Penso che sia meglio integrare il tuo .h nella risposta accettata, quindi sarà facile per le altre persone trovare l'intera soluzione in un unico posto;) – bluish

+0

Ho dovuto aggiungere "typename'" davanti alla 'mappa :: const_iterator' dichiarazione per sbarazzarsi di un errore del compilatore di dichiarazione non valido. Altrimenti funziona alla grande. – tbc

+0

@tbc sentitevi liberi di aggiornare la mia risposta se trovate errori :) – javapowered

0

È possibile calcolare l'hash di la stringa e quindi utilizzare questo:

template <typename H, typename E> 
E map_hash(H const key, std::initializer_list<std::pair<H, E>> const il) 
{ 
    auto const i(
    std::find_if(il.begin(), 
     il.end(), 
     [key](auto& p) 
     { 
     return p.first == key; 
     } 
    ) 
); 

    assert(i != il.end()); 

    return i->second; 
}