type name example enum ejemplo c++ templates enums

c++ - name - typedef enum c



Cómo mapear fácilmente enums de C++ a cadenas (17)

Tengo un montón de tipos enum en algunos archivos de encabezado de biblioteca que estoy usando, y quiero tener una forma de convertir valores enum a cadenas de usuario, y viceversa.

RTTI no lo hará por mí, porque las ''cadenas de usuario'' deben ser un poco más legibles que las enumeraciones.

Una solución de fuerza bruta sería un conjunto de funciones como esta, pero creo que es un poco demasiado parecido a C.

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

Tengo la intuición de que hay una solución elegante usando plantillas, pero todavía no puedo entenderlo.

ACTUALIZACIÓN: Gracias por las sugerencias: debería haber dejado en claro que las enumeraciones están definidas en un encabezado de biblioteca de un tercero, por lo que no quiero tener que cambiar la definición de ellas.

Mi intuición ahora es evitar plantillas y hacer algo como esto:

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


Aquí hay un intento de obtener << y >> operadores de flujo en enum automáticamente con un comando de macro de una sola línea ...

Definiciones:

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

Sin embargo, no estoy seguro sobre las limitaciones de este esquema ... ¡los comentarios son bienvenidos!


Estaría tentado de tener un mapa m, e incluirlo en la enumeración.

configuración con m [MyEnum.VAL1] = "Value 1";

y todo está hecho.


Genera automáticamente una forma de otra.

Fuente:

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

Generado:

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

Si los valores de enum son grandes, entonces un formulario generado podría usar unordered_map <> o plantillas como lo sugiere Constantin.

Fuente:

enum State{ state0 = 0, /* state 0 */ state1 = 1, /* state 1 */ state2 = 2, /* state 2 */ state3 = 4, /* state 3 */ state16 = 0x10000, /* state 16 */ };

Generado:

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

Ejemplo:

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


He requerido esta funcionalidad varias veces para depurar / analizar código de otros. Para esto, he escrito un script Perl que genera una clase con varios métodos sobrecargados toString . Cada método toString toma un Enum como argumento y devuelve const char* .

Por supuesto, el script no analiza C ++ para las enumeraciones en sí, sino que usa ctags para generar la tabla de símbolos.

El script de Perl está aquí: http://heinitz-it.de/download/enum2string/enum2string.pl.html


La solución de MSalters es buena, pero básicamente implementa de nuevo boost::assign::map_list_of . Si tiene impulso, puede usarlo directamente:

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


Recientemente tuve el mismo problema con una librería de proveedores (Fincad). Afortunadamente, el proveedor proporcionó doucumentation xml para todas las enumeraciones. Terminé generando un mapa para cada tipo de enumeración y proporcionando una función de búsqueda para cada enumeración. Esta técnica también te permite interceptar una búsqueda fuera del rango de la enumeración.

Estoy seguro de que Swig podría hacer algo similar por ti, pero me complace proporcionarte utilidades de generación de código que están escritas en rubí.

Aquí hay una muestra del código:

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

Parece que quieres ir por el otro camino (enum a string, en lugar de string a enum), pero esto debería ser trivial para revertir.

-Pizca


Recuerdo haber respondido esto en otro lugar en . Repitiéndolo aquí. Básicamente es una solución basada en macros variadas, y es bastante fácil de usar:

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

Para usarlo en tu código, simplemente hazlo:

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


Si desea obtener representaciones de MyEnum variables de MyEnum , las plantillas no lo cortarán. La plantilla se puede especializar en valores integrales conocidos en tiempo de compilación.

Sin embargo, si eso es lo que quieres, intenta:

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

Esto es detallado, pero detectará errores como el que hizo en cuestión: su case VAL1 está duplicado.


Si desea que los enum se nombren a sí mismos como cadenas, consulte esta publicación . De lo contrario, un std::map<MyEnum, char const*> funcionará muy bien. (No tiene sentido copiar los literales de cadena a std :: strings en el mapa)

Para obtener más azúcar sintáctico, a continuación se explica cómo escribir una clase map_init. El objetivo es permitir

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

La template <typename T> map_init(T&) función template <typename T> map_init(T&) devuelve un map_init_helper<T> . map_init_helper<T> almacena un T & y define el trivial map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&) . (Devolver *this desde el operator() permite el encadenamiento del operator() , como el operator<< en std::ostream s)

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

Como la función y la clase auxiliar están modeladas, puede usarlas para cualquier mapa o estructura tipo mapa. Es decir, también puede agregar entradas a std::unordered_map

Si no le gusta escribir estos ayudantes, boost :: assign ofrece la misma funcionalidad de forma predeterminada.


Solo quería mostrar esta posible solución elegante usando macros. Esto no resuelve el problema, pero creo que es una buena manera de volver a hablar sobre el 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; }

---- EDIT ----

Después de algunas investigaciones en Internet y algunos problemas propios, llegué a la siguiente solución:

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

Solo quería publicarlo, tal vez alguien podría encontrar esta solución útil. No hay necesidad de clases de plantillas sin necesidad de c + + 11 y no hay necesidad de aumentar, por lo que esto también podría usarse para una simple C.

---- EDIT2 ----

la tabla de información puede producir algunos problemas cuando se usan más de 2 enumeraciones (problema del compilador). La siguiente solución alternativa funcionó:

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


Sugiero que una combinación de usar X-macros sea la mejor solución y las siguientes funciones de plantilla:

Para pedir prestado de marcinkoziukmyopenidcom y extendido

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)


Tus respuestas me inspiraron a escribir algunas macros yo mismo. Mis requisitos fueron los siguientes:

  1. solo escriba cada valor de la enumeración una vez, por lo que no hay listas dobles para mantener

  2. no guarde los valores enum en un archivo separado que más tarde sea #incluido, para que pueda escribirlo donde quiera

  3. no reemplace la enumeración en sí, todavía quiero tener definido el tipo de enumeración, pero además de eso, quiero poder asignar cada nombre enum a la cadena correspondiente (para no afectar el código heredado)

  4. la búsqueda debe ser rápida, preferiblemente sin switch-case, para esas enormes enumeraciones

Este código crea una enumeración clásica con algunos valores. Además crea como std :: map que mapea cada valor enum a su nombre (es decir, mapa [E_SUNDAY] = "E_SUNDAY", etc.)

Ok, aquí está el código ahora:

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 // este es el archivo que desea incluir cada vez que necesite hacer esto, utilizará las macros que contiene:

#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 // este es un ejemplo de cómo usarlo para crear una enumeración personalizada:

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

Aclamaciones.


Uso this solución que reproduzco a continuación:

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


Vea si la siguiente sintaxis le conviene:

// 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

Si lo hace, entonces es posible que desee ver este artículo:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


en el encabezado:

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

en el archivo .cpp:

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

Advertencia: no maneje el índice de matriz malo. :) Pero puede agregar fácilmente una función para verificar la enumeración antes de obtener la cadena de la matriz.


enum MyEnum { VAL1, VAL2,VAL3 }; #define StringMyEnum(e) ({__unused MyEnum _e = e;std::string(#e);}) #define CStringMyEnum(e) ({__unused MyEnum _e = e;#e;})

__unused es un atributo en GCC / LLVM, entonces puedes usarlo así:

std::string s = StringMyEnum(VAL1); const char *c = CStringMyEnum(VAL1);


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

Arriba está mi solución simple. Uno de sus beneficios es el ''NUM'' que controla el tamaño de la matriz de mensajes, también evita el acceso fuera del límite (si lo usa con prudencia).

También puede definir una función para obtener la cadena:

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

Además de mi solución, encontré la siguiente bastante interesante. En general, resolvió el problema de sincronización del anterior.

Diapositivas aquí: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Codifique aquí: https://github.com/arunksaha/enum_to_string