2009-04-07 17 views
17

¿Hay alguna manera de asociar una cadena de un archivo de texto con un valor enum?Cadena de enumeración en C++

El problema es: Tengo algunos valores enum almacenados como cadena en un archivo de texto que leo sobre la marcha al cumplir alguna condición ... Ahora quiero asignar el valor de lectura a una enumeración.

¿Cuál es la forma más efectiva de hacerlo? No necesita ser el enfoque más simple. .

Respuesta

31

Puede configurar un mapa que puede utilizar una y otra vez:

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

¿Por qué no usar un hasmap? ¿Sería la búsqueda más eficiente? Bueno, debe probar y medir la efectividad, ya que puede depender del número de elementos en su enumeración ... Con gran enumeración, hasmap podría hacer que sea una forma más rápida de obtener el valor, ¿no es así? –

+1

hash_map no es parte del estándar (y unordered_map solo se agregó en TR1). Además, es probable que haya muy poca diferencia entre hash_map y el mapa a menos que comience a tener una gran cantidad de valores enum.En cualquier caso, es un cambio fácil si los perfiles indican que algo es demasiado lento – Eclipse

+1

el mapa hash sería muy ineficiente para la mayoría de las enumeraciones que tienen un número muy limitado de valores. De hecho, la búsqueda lineal puede ser más eficiente que un hash-map si la enumeración solo muestra los 8 valores normales. Si tiene 2 o 3, la búsqueda lineal probablemente también vencerá a std :: map. – CashCow

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

analizar la cadena de sí mismo, coincide con la cadena de valor (que también es un índice a una map<string, enum>

2

el uso de un std::map plantea la pregunta: ¿cómo obtener el mapa inicializado prefiero usar una función:

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

¿cómo es la inicialización del mapa un problema? –

+0

Debe asegurarse de que esté listo antes de usarlo para asignar las cadenas. Esto es posible, pero solo escribiendo una función. –

+0

Mi inclinación es hacia el mapa, ya que la búsqueda es rápida, pero Neil, su respuesta me hizo pensar: las enumeraciones se conocen en tiempo de compilación, al igual que sus representaciones de cadenas. ¿Existe una manera eficiente y escalable de implementar todo esto en tiempo de compilación? – veefu

3

Estoy de acuerdo con muchas de las respuestas que std::map es la solución más fácil.

Si necesita algo más rápido, puede usar un mapa hash. Quizás su compilador ya ofrezca uno, como hash_map o el próximo estándar unordered_map, o puede obtener uno de boost. Cuando se conocen todas las cadenas antes de tiempo, también se puede usar perfect hashing.

+1

No estoy de acuerdo con que hash_map sea más rápido en general; sin embargo, si sus nombres son "correctos", puede utilizar alguna propiedad especial que conozca sobre ellos, p. Ej. todos son únicos en la tercera carta, o en alguna otra combinación. Luego cambie o lo que sea en el valor que genere. Si está procesando algún tipo de alimentación con grandes cantidades de valores codificados que se incluirán en un conjunto limitado, esto puede brindarle beneficios de rendimiento, que es lo que creo que quiere decir con un hashing perfecto. – CashCow

+1

@CashCow, no hay necesidad de adivinar lo que quise decir con "hashing perfecto", la frase contiene un enlace a Wikipedia donde está definido. Estaré de acuerdo con usted en que no puede hacer suposiciones generales sobre la velocidad, y probablemente debería haberlo dicho en la respuesta. –

2

Mire Boost.Bimap, proporciona asociaciones bidireccionales entre dos conjuntos de valores. También puede elegir el contenedor subyacente.

1

¿Esto es lo que quieres? La inicialización es recta, y no se necesita instanciación.

uso:

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); 
    ... 
} 

biblioteca:

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

respuesta aceptada no contiene la lista completa. Estoy agregando EnumParser.h la que he creado a partir de la respuesta aceptada, espero que pueda ayudar a

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

uso es simple:

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

Creo que es mejor integrar su .h en la respuesta aceptada, por lo que será fácil para otras personas encontrar la solución completa en un solo lugar;) – bluish

+0

Tuve que agregar '' typename' 'delante del 'map :: declaración de const_iterator' para deshacerse de un error de compilador de declaración no válida. De lo contrario, funciona muy bien. – tbc

+0

@tbc no dude en actualizar mi respuesta si encuentra errores :) – javapowered

0

Se puede calcular el hash de la cadena y luego usar esto:

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