type parse into getvalues from enum convertir convert c++ string enums scripting
http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=&

parse - ¿Hay una manera simple de convertir C++ enum en una cadena?



get enum from string c# (30)

Supongamos que tenemos algunas enumeraciones nombradas:

enum MyEnum { FOO, BAR = 0x50 };

Lo que busqué en Google es un script (cualquier idioma) que escanea todos los encabezados de mi proyecto y genera un encabezado con una función por enumeración.

char* enum_to_string(MyEnum t);

Y una implementación con algo como esto:

char* enum_to_string(MyEnum t){ switch(t){ case FOO: return "FOO"; case BAR: return "BAR"; default: return "INVALID ENUM"; } }

El gotcha es realmente con enumeraciones definidas por tipo, y enumeraciones de estilo C sin nombre. ¿Alguien sabe algo de esto?

EDIT: la solución no debe modificar mi fuente, a excepción de las funciones generadas. Las enumeraciones están en una API, por lo que utilizar las soluciones propuestas hasta ahora no es una opción.


@hydroo: Sin el archivo adicional:

#define SOME_ENUM(DO) / DO(Foo) / DO(Bar) / DO(Baz) #define MAKE_ENUM(VAR) VAR, enum MetaSyntacticVariable{ SOME_ENUM(MAKE_ENUM) }; #define MAKE_STRINGS(VAR) #VAR, const char* const MetaSyntacticVariableNames[] = { SOME_ENUM(MAKE_STRINGS) };


Acabo de reinventar esta rueda hoy, y pensé en compartirla.

Esta implementación no requiere ningún cambio en el código que define las constantes, que pueden ser enumeraciones o #define s o cualquier otra cosa que dependa de un entero; en mi caso, tenía símbolos definidos en términos de otros símbolos. También funciona bien con valores dispersos. Incluso permite múltiples nombres para el mismo valor, devolviendo siempre el primero. El único inconveniente es que se requiere que hagas una tabla de las constantes, que pueden quedar desactualizadas a medida que se agregan nuevas, por ejemplo.

struct IdAndName { int id; const char * name; bool operator<(const IdAndName &rhs) const { return id < rhs.id; } }; #define ID_AND_NAME(x) { x, #x } const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end) { if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id) std::stable_sort(table_begin, table_end); IdAndName searchee = { id, NULL }; IdAndName *p = std::lower_bound(table_begin, table_end, searchee); return (p == table_end || p->id != id) ? NULL : p->name; } template<int N> const char * IdToName(int id, IdAndName (&table)[N]) { return IdToName(id, &table[0], &table[N]); }

Un ejemplo de cómo lo usarías:

static IdAndName WindowsErrorTable[] = { ID_AND_NAME(INT_MAX), // flag value to indicate unsorted table ID_AND_NAME(NO_ERROR), ID_AND_NAME(ERROR_INVALID_FUNCTION), ID_AND_NAME(ERROR_FILE_NOT_FOUND), ID_AND_NAME(ERROR_PATH_NOT_FOUND), ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES), ID_AND_NAME(ERROR_ACCESS_DENIED), ID_AND_NAME(ERROR_INVALID_HANDLE), ID_AND_NAME(ERROR_ARENA_TRASHED), ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY), ID_AND_NAME(ERROR_INVALID_BLOCK), ID_AND_NAME(ERROR_BAD_ENVIRONMENT), ID_AND_NAME(ERROR_BAD_FORMAT), ID_AND_NAME(ERROR_INVALID_ACCESS), ID_AND_NAME(ERROR_INVALID_DATA), ID_AND_NAME(ERROR_INVALID_DRIVE), ID_AND_NAME(ERROR_CURRENT_DIRECTORY), ID_AND_NAME(ERROR_NOT_SAME_DEVICE), ID_AND_NAME(ERROR_NO_MORE_FILES) }; const char * error_name = IdToName(GetLastError(), WindowsErrorTable);

La función IdToName basa en std::lower_bound para hacer búsquedas rápidas, lo que requiere que la tabla sea ordenada. Si las primeras dos entradas de la tabla están desordenadas, la función lo ordenará automáticamente.

Editar: Un comentario me hizo pensar en otra forma de utilizar el mismo principio. Una macro simplifica la generación de una declaración de switch grande.

#define ID_AND_NAME(x) case x: return #x const char * WindowsErrorToName(int id) { switch(id) { ID_AND_NAME(ERROR_INVALID_FUNCTION); ID_AND_NAME(ERROR_FILE_NOT_FOUND); ID_AND_NAME(ERROR_PATH_NOT_FOUND); ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES); ID_AND_NAME(ERROR_ACCESS_DENIED); ID_AND_NAME(ERROR_INVALID_HANDLE); ID_AND_NAME(ERROR_ARENA_TRASHED); ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY); ID_AND_NAME(ERROR_INVALID_BLOCK); ID_AND_NAME(ERROR_BAD_ENVIRONMENT); ID_AND_NAME(ERROR_BAD_FORMAT); ID_AND_NAME(ERROR_INVALID_ACCESS); ID_AND_NAME(ERROR_INVALID_DATA); ID_AND_NAME(ERROR_INVALID_DRIVE); ID_AND_NAME(ERROR_CURRENT_DIRECTORY); ID_AND_NAME(ERROR_NOT_SAME_DEVICE); ID_AND_NAME(ERROR_NO_MORE_FILES); default: return NULL; } }


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

Not sure about the limitations of this scheme though... comments are welcome!


Aquí hay un programa de CLI que escribí para convertir fácilmente enumeraciones en cadenas. Es fácil de usar, y tarda unos 5 segundos en hacerlo (incluido el tiempo de cd al directorio que contiene el programa, luego lo ejecuta y le pasa el archivo que contiene la enumeración).

Descargue aquí: http://www.mediafire.com/?nttignoozzz

El tema de discusión aquí: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Ejecute el programa con el argumento "--help" para obtener una descripción de cómo usarlo.


Aquí una solución de un archivo (basada en la respuesta elegante de @Marcin:

#include <iostream> #define ENUM_TXT / X(Red) / X(Green) / X(Blue) / X(Cyan) / X(Yellow) / X(Magenta) / enum Colours { # define X(a) a, ENUM_TXT # undef X ColoursCount }; char const* const colours_str[] = { # define X(a) #a, ENUM_TXT # undef X 0 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c] << std::endl; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; }


Como variante, use lib simple> http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

En el código

#include <EnumString.h> enum FORM { F_NONE = 0, F_BOX, F_CUBE, F_SPHERE, };

agregar líneas

Begin_Enum_String( FORM ) { Enum_String( F_NONE ); Enum_String( F_BOX ); Enum_String( F_CUBE ); Enum_String( F_SPHERE ); } End_Enum_String;

Funciona bien, si los valores en enum no son duplicados .

Ejemplo de uso

enum FORM f = ... const std::string& str = EnumString< FORM >::From( f );

y viceversa

assert( EnumString< FORM >::To( f, str ) );


Es posible que desee comprobar GCCXML .

Ejecutar GCCXML en su código de muestra produce:

<GCC_XML> <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/> <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/> <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1"> <EnumValue name="FOO" init="0"/> <EnumValue name="BAR" init="80"/> </Enumeration> <File id="f0" name="my_enum.h"/> </GCC_XML>

Puede usar cualquier idioma que prefiera para extraer las etiquetas Enumeration y EnumValue y generar el código deseado.


Es un software inédito pero parece que BOOST_ENUM de Frank Laub podría encajar en la factura. La parte que me gusta es que puedes definir una enumeración dentro del alcance de una clase que la mayoría de las enumeraciones basadas en macros normalmente no te dejan hacer. Está ubicado en Boost Vault en: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& No ha visto ningún desarrollo desde 2006, así que no saber qué tan bien se compila con las nuevas versiones de Boost. Busque en libs / test un ejemplo de uso.


Esa es prácticamente la única forma en que se puede hacer (una serie de cadenas también podría funcionar).

El problema es que, una vez que se compila un programa C, el valor binario de la enumeración es todo lo que se usa y el nombre desaparece.


Esta es una modificación de @ user3360260 respuesta. Tiene las siguientes características nuevas

  • MyEnum fromString(const string&) soporte
  • compila con VisualStudio 2012
  • el enum es un tipo real de POD (no solo declaraciones const), por lo que puede asignarlo a una variable.
  • Se agregó una característica de "rango" de C ++ (en forma de vector) para permitir la iteración de "foreach" sobre enum

Uso:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) MyEnum foo = MyEnum::TWO; cout << MyEnum::toString(foo); // static method cout << foo.toString(); // member method cout << MyEnum::toString(MyEnum::TWO); cout << MyEnum::toString(10); MyEnum foo = myEnum::fromString("TWO"); // C++11 iteration over all values for( auto x : MyEnum::allValues() ) { cout << x.toString() << endl; }

Aquí está el código

#define SMART_ENUM(EnumName, ...) / class EnumName / { / public: / EnumName() : value(0) {} / EnumName(int x) : value(x) {} / public: / enum {__VA_ARGS__}; / private: / static void initMap(std::map<int, std::string>& tmp) / { / using namespace std; / / int val = 0; / string buf_1, buf_2, str = #__VA_ARGS__; / replace(str.begin(), str.end(), ''='', '' ''); / stringstream stream(str); / vector<string> strings; / while (getline(stream, buf_1, '','')) / strings.push_back(buf_1); / for(vector<string>::iterator it = strings.begin(); / it != strings.end(); / ++it) / { / buf_1.clear(); buf_2.clear(); / stringstream localStream(*it); / localStream>> buf_1 >> buf_2; / if(buf_2.size() > 0) / val = atoi(buf_2.c_str()); / tmp[val++] = buf_1; / } / } / int value; / public: / operator int () const { return value; } / std::string toString(void) const { / return toString(value); / } / static std::string toString(int aInt) / { / return nameMap()[aInt]; / } / static EnumName fromString(const std::string& s) / { / auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& p) { / return p.second == s; / }); / if (it == nameMap().end()) { / /*value not found*/ / throw EnumName::Exception(); / } else { / return EnumName(it->first); / } / } / class Exception : public std::exception {}; / static std::map<int,std::string>& nameMap() { / static std::map<int,std::string> nameMap0; / if (nameMap0.size() ==0) initMap(nameMap0); / return nameMap0; / } / static std::vector<EnumName> allValues() { / std::vector<EnumName> x{ __VA_ARGS__ }; / return x; / } / bool operator<(const EnumName a) const { return (int)*this < (int)a; } / };

Tenga en cuenta que la conversión a String es una búsqueda rápida, mientras que la conversión de String es una búsqueda lineal lenta. Pero las cadenas son tan caras de todos modos (y el archivo asociado IO), no sentí la necesidad de optimizar o usar un bimap.


Esta fue mi solución con BOOST:

#include <boost/preprocessor.hpp> #define X_STR_ENUM_TOSTRING_CASE(r, data, elem) / case elem : return BOOST_PP_STRINGIZE(elem); #define X_ENUM_STR_TOENUM_IF(r, data, elem) / else if(data == BOOST_PP_STRINGIZE(elem)) return elem; #define STR_ENUM(name, enumerators) / enum name { / BOOST_PP_SEQ_ENUM(enumerators) / }; / / inline const QString enumToStr(name v) / { / switch (v) / { / BOOST_PP_SEQ_FOR_EACH( / X_STR_ENUM_TOSTRING_CASE, / name, / enumerators / ) / / default: / return "[Unknown " BOOST_PP_STRINGIZE(name) "]"; / } / } / / template <typename T> / inline const T strToEnum(QString v); / / template <> / inline const name strToEnum(QString v) / { / if(v=="") / throw std::runtime_error("Empty enum value"); / / BOOST_PP_SEQ_FOR_EACH( / X_ENUM_STR_TOENUM_IF, / v, / enumerators / ) / / else / throw std::runtime_error( / QString("[Unknown value %1 for enum %2]") / .arg(v) / .arg(BOOST_PP_STRINGIZE(name)) / .toStdString().c_str()); / }

Para crear enum, declare:

STR_ENUM ( SERVICE_RELOAD, (reload_log) (reload_settings) (reload_qxml_server) )

Para conversiones:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log"); QString serviceReloadStr = enumToStr(reload_log);


Esto se puede hacer en C ++ 11

#include <map> enum MyEnum { AA, BB, CC, DD }; static std::map< MyEnum, const char * > info = { {AA, "This is an apple"}, {BB, "This is a book"}, {CC, "This is a coffee"}, {DD, "This is a door"} }; void main() { std::cout << info[AA] << endl << info[BB] << endl << info[CC] << endl << info[DD] << endl; }


Interesante para ver la cantidad de formas. Aquí hay uno que utilicé hace mucho tiempo:

en el archivo myenummap.h:

#include <map> #include <string> enum test{ one, two, three, five=5, six, seven }; struct mymap : std::map<unsigned int, std::string> { mymap() { this->operator[]( one ) = "ONE"; this->operator[]( two ) = "TWO"; this->operator[]( three ) = "THREE"; this->operator[]( five ) = "FIVE"; this->operator[]( six ) = "SIX"; this->operator[]( seven ) = "SEVEN"; }; ~mymap(){}; };

en main.cpp

#include "myenummap.h" ... mymap nummap; std::cout<< nummap[ one ] << std::endl;

No es const, pero es conveniente.

Aquí hay otra manera que usa las características de C ++ 11. Esto es const, no hereda un contenedor STL y es un poco más ordenado:

#include <vector> #include <string> #include <algorithm> #include <iostream> //These stay together and must be modified together enum test{ one, two, three, five=5, six, seven }; std::string enum_to_str(test const& e) { typedef std::pair<int,std::string> mapping; auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; std::vector<mapping> const nummap = { m(one,"one"), m(two,"two"), m(three,"three"), m(five,"five"), m(six,"six"), m(seven,"seven"), }; for(auto i : nummap) { if(i.first==static_cast<int>(e)) { return i.second; } } return ""; } int main() { // std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"<<std::endl; //returns an empty string std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works return 0; }


La siguiente secuencia de comandos de ruby ​​intenta analizar los encabezados y construye las fuentes necesarias junto con los encabezados originales.

#! /usr/bin/env ruby # Let''s "parse" the headers # Note that using a regular expression is rather fragile # and may break on some inputs GLOBS = [ "toto/*.h", "tutu/*.h", "tutu/*.hxx" ] enums = {} GLOBS.each { |glob| Dir[glob].each { |header| enums[header] = File.open(header, ''rb'') { |f| f.read }.scan(/enum/s+(/w+)/s+/{/s*([^}]+?)/s*/}/m).collect { |enum_name, enum_key_and_values| [ enum_name, enum_key_and_values.split(//s*,/s*/).collect { |enum_key_and_value| enum_key_and_value.split(//s*=/s*/).first } ] } } } # Now we build a .h and .cpp alongside the parsed headers # using the template engine provided with ruby require ''erb'' template_h = ERB.new <<-EOS #ifndef <%= enum_name %>_to_string_h_ #define <%= enum_name %>_to_string_h_ 1 #include "<%= header %>" char* enum_to_string(<%= enum_name %> e); #endif EOS template_cpp = ERB.new <<-EOS #include "<%= enum_name %>_to_string.h" char* enum_to_string(<%= enum_name %> e) { switch (e) {<% enum_keys.each do |enum_key| %> case <%= enum_key %>: return "<%= enum_key %>";<% end %> default: return "INVALID <%= enum_name %> VALUE"; } } EOS enums.each { |header, enum_name_and_keys| enum_name_and_keys.each { |enum_name, enum_keys| File.open("#{File.dirname(header)}/#{enum_name}_to_string.h", ''wb'') { |built_h| built_h.write(template_h.result(binding)) } File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", ''wb'') { |built_cpp| built_cpp.write(template_cpp.result(binding)) } } }

El uso de expresiones regulares hace que este "analizador" sea bastante frágil, puede que no sea capaz de manejar sus encabezados específicos correctamente.

Digamos que tiene un encabezado toto / ah, que contiene definiciones para enumeraciones MyEnum y MyEnum2. El script se compilará

toto/MyEnum_to_string.h toto/MyEnum_to_string.cpp toto/MyEnum2_to_string.h toto/MyEnum2_to_string.cpp

Las soluciones más robustas serían:

  • Cree todas las fuentes que definen las enumeraciones y sus operaciones desde otra fuente. Esto significa que definirá sus enumeraciones en un archivo XML / YML / lo que es mucho más fácil de analizar que C / C ++.
  • Use un compilador real como el sugerido por Avdi.
  • Use macros de preprocesador con o sin plantillas.

Lo hago con clases de envoltura enum lado a lado separadas que se generan con macros. Hay varias ventajas:

  • Puede generarlos para enumeraciones que no defino (por ejemplo: enumeraciones de encabezado de plataforma OS)
  • Puede incorporar la verificación de rango en la clase contenedora
  • Puede hacer un formateo "más inteligente" con enums de campo de bit

La desventaja, por supuesto, es que necesito duplicar los valores enum en las clases de formateador, y no tengo ningún script para generarlos. Aparte de eso, parece funcionar bastante bien.

Aquí hay un ejemplo de una enumeración de mi base de código, sin el código de marco que implementa las macros y las plantillas, pero puede obtener la idea:

enum EHelpLocation { HELP_LOCATION_UNKNOWN = 0, HELP_LOCAL_FILE = 1, HELP_HTML_ONLINE = 2, }; class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation > { public: static inline CString FormatEnum( EHelpLocation eValue ) { switch ( eValue ) { ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE ); default: return FormatAsNumber( eValue ); } } }; DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > ); typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation;

La idea es, en vez de usar EHelpLocation, usar SEHelpLocation; todo funciona igual, pero obtienes un control de rango y un método ''Format ()'' en la variable enum. Si necesita formatear un valor independiente, puede usar CEnumFormatter_EHelpLocation :: FormatEnum (...).

Espero que esto sea útil. Me doy cuenta de que esto tampoco aborda la pregunta original sobre un guión para generar realmente la otra clase, pero espero que la estructura ayude a alguien a tratar de resolver el mismo problema, o escriba ese guión.


Lo que tiendo a hacer es crear una matriz C con los nombres en el mismo orden y posición que los valores enum.

p.ej.

enum colours { red, green, blue }; const char *colour_names[] = { "red", "green", "blue" };

entonces puede usar la matriz en lugares donde desee un valor legible para el ser humano, por ejemplo

colours mycolour = red; cout << "the colour is" << colour_names[mycolour];

Puede experimentar un poco con el operador de stringing (vea # en su referencia de preprocesador) que hará lo que quiera, en algunas circunstancias, por ejemplo:

#define printword(XX) cout << #XX; printword(red);

imprimirá "rojo" a stdout. Lamentablemente, no funcionará para una variable (ya que obtendrá el nombre de la variable impreso)


No hace mucho tiempo hice un truco para que las enumeraciones se mostraran correctamente en QComboBox y para tener una definición de enum y representaciones de cadenas como una declaración

#pragma once #include <boost/unordered_map.hpp> namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map<int, std::wstring> kv_storage_t; typedef kv_storage_t::value_type kv_type; kv_storage_t const & kv() const { return storage_; } LPCWSTR name(int i) const { kv_storage_t::const_iterator it = storage_.find(i); if(it != storage_.end()) return it->second.c_str(); return L"empty"; } protected: kv_storage_t storage_; }; template<class T> struct enumerator; template<class D> struct enum_singleton : enumerator_base { static enumerator_base const & instance() { static D inst; return inst; } }; } #define QENUM_ENTRY(K, V, N) K, N storage_.insert(std::make_pair((int)K, V)); #define QBEGIN_ENUM(NAME, C) / enum NAME / { / C / } / }; / } / #define QEND_ENUM(NAME) / }; / namespace enumeration / { / template<> / struct enumerator<NAME>/ : enum_singleton< enumerator<NAME> >/ { / enumerator() / { //usage /* QBEGIN_ENUM(test_t, QENUM_ENTRY(test_entry_1, L"number uno", QENUM_ENTRY(test_entry_2, L"number dos", QENUM_ENTRY(test_entry_3, L"number tres", QEND_ENUM(test_t))))) */

Ahora tiene enumeration::enum_singleton<your_enum>::instance() capaz de convertir enumeraciones a cadenas. Si reemplaza kv_storage_t con boost::bimap , también podrá realizar la conversión hacia atrás. Se introdujo la clase base común para convertidor para almacenarlo en el objeto Qt, porque los objetos Qt no podían ser plantillas

Apariencia previa


Otra respuesta: en algunos contextos, tiene sentido definir su enumeración en un formato que no sea de código, como un archivo CSV, YAML o XML, y luego generar tanto el código de enumeración de C ++ como el código de cadena de la definición. Este enfoque puede o no ser práctico en su aplicación, pero es algo a tener en cuenta.


QT es capaz de extraer eso de (gracias al compilador de metaobjetos): link


Tenga en cuenta que su función de conversión idealmente debería ser devolver un const char *.

Si puede permitirse poner sus enumeraciones en sus archivos de encabezado separados, quizás pueda hacer algo como esto con macros (oh, esto será feo):

#include "enum_def.h" #include "colour.h" #include "enum_conv.h" #include "colour.h"

Donde enum_def.h tiene:

#undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) enum NAME { #define ENUM_ADD(NAME, VALUE) NAME = VALUE, #define ENUM_END };

Y enum_conv.h tiene:

#undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) const char *##NAME##_to_string(NAME val) { switch (val) { #define ENUM_ADD(NAME, VALUE) case NAME: return #NAME; #define ENUM_END default: return "Invalid value"; } }

Y finalmente, colour.h tiene:

ENUM_START(colour) ENUM_ADD(red, 0xff0000) ENUM_ADD(green, 0x00ff00) ENUM_ADD(blue, 0x0000ff) ENUM_END

Y puede usar la función de conversión como:

printf("%s", colour_to_string(colour::red));

Esto es feo, pero es la única manera (en el nivel de preprocesador) que le permite definir su enumeración en un solo lugar en su código. Por lo tanto, su código no es propenso a errores debido a modificaciones en la enumeración. Tu definición de enum y la función de conversión siempre estarán sincronizadas. Sin embargo, repito, esto es feo :)


Tengo una macro increíblemente fácil de usar que hace esto de una manera completamente seca. Se trata de macros variadas y algunos simples análisis de magia. Aquí va:

#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 usar esto en tu código, simplemente hazlo:

AWESOME_MAKE_ENUM(Animal, DOG, CAT, HORSE );


Un problema con la respuesta 0 es que los valores binarios enum no necesariamente comienzan en 0 y no son necesariamente contiguos.

Cuando lo necesito, generalmente:

  • tire de la definición enum en mi fuente
  • edítelo para obtener solo los nombres
  • haga una macro para cambiar el nombre a la cláusula de caso en la pregunta, aunque típicamente en una línea: caso foo: return "foo";
  • agregue el interruptor, predeterminado y otra sintaxis para hacerlo legal

X-macros son la mejor solución. Ejemplo:

#include <iostream> 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 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c]; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; }

colours.def:

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

Sin embargo, generalmente prefiero el siguiente método, por lo que es posible modificar un poco la cadena.

#define X(a, b) a, #define X(a, b) b, X(Red, "red") X(Green, "green") // etc.


La solución macro de Suma es agradable. Sin embargo, no necesita tener dos macro diferentes. C ++ felizmente incluirá un encabezado dos veces. Solo deja afuera el protector de inclusión.

Entonces tendrías un foobar.h definiendo solo

ENUM(Foo, 1) ENUM(Bar, 2)

y lo incluirías así:

#define ENUMFACTORY_ARGUMENT "foobar.h" #include "enumfactory.h"

enumfactory.h hará 2 #include ENUMFACTORY_ARGUMENT s. En la primera ronda, expande ENUM como DECLARE_ENUM de Suma; en la segunda ronda, ENUM funciona como DEFINE_ENUM .

También puede incluir enumfactory.h varias veces, siempre y cuando pase en # definiciones diferentes para ENUMFACTORY_ARGUMENT


Check this post:

Class implementation of C++ Enums

it contains class implementation of c++ enum.


I want to post this in case someone finds it useful.

In my case, I simply need to generate ToString() and FromString() functions for a single C++11 enum from a single .hpp file.

I wrote a python script that parses the header file containing the enum items and generates the functions in a new .cpp file.

You can add this script in CMakeLists.txt with execute_process , or as a pre-build event in Visual Studio. The .cpp file will be automatically generated, without the need to manually update it each time a new enum item is added.

generate_enum_strings.py

# This script is used to generate strings from C++ enums import re import sys import os fileName = sys.argv[1] enumName = os.path.basename(os.path.splitext(fileName)[0]) with open(fileName, ''r'') as f: content = f.read().replace(''/n'', '''') searchResult = re.search(''enum(.*)/{(.*?)/};'', content) tokens = searchResult.group(2) tokens = tokens.split('','') tokens = map(str.strip, tokens) tokens = map(lambda token: re.search(''([a-zA-Z0-9_]*)'', token).group(1), tokens) textOut = '''' textOut += ''/n#include "'' + enumName + ''.hpp"/n/n'' textOut += ''namespace myns/n'' textOut += ''{/n'' textOut += '' std::string ToString(ErrorCode errorCode)/n'' textOut += '' {/n'' textOut += '' switch (errorCode)/n'' textOut += '' {/n'' for token in tokens: textOut += '' case '' + enumName + ''::'' + token + '':/n'' textOut += '' return "'' + token + ''";/n'' textOut += '' default:/n'' textOut += '' return "Last";/n'' textOut += '' }/n'' textOut += '' }/n'' textOut += ''/n'' textOut += '' '' + enumName + '' FromString(const std::string &errorCode)/n'' textOut += '' {/n'' textOut += '' if ("'' + tokens[0] + ''" == errorCode)/n'' textOut += '' {/n'' textOut += '' return '' + enumName + ''::'' + tokens[0] + '';/n'' textOut += '' }/n'' for token in tokens[1:]: textOut += '' else if("'' + token + ''" == errorCode)/n'' textOut += '' {/n'' textOut += '' return '' + enumName + ''::'' + token + '';/n'' textOut += '' }/n'' textOut += ''/n'' textOut += '' return '' + enumName + ''::Last;/n'' textOut += '' }/n'' textOut += ''}/n'' fileOut = open(enumName + ''.cpp'', ''w'') fileOut.write(textOut)

Ejemplo:

ErrorCode.hpp

#pragma once #include <string> #include <cstdint> namespace myns { enum class ErrorCode : uint32_t { OK = 0, OutOfSpace, ConnectionFailure, InvalidJson, DatabaseFailure, HttpError, FileSystemError, FailedToEncrypt, FailedToDecrypt, EndOfFile, FailedToOpenFileForRead, FailedToOpenFileForWrite, FailedToLaunchProcess, Last }; std::string ToString(ErrorCode errorCode); ErrorCode FromString(const std::string &errorCode); }

Run python generate_enum_strings.py ErrorCode.hpp

Resultado:

ErrorCode.cpp

#include "ErrorCode.hpp" namespace myns { std::string ToString(ErrorCode errorCode) { switch (errorCode) { case ErrorCode::OK: return "OK"; case ErrorCode::OutOfSpace: return "OutOfSpace"; case ErrorCode::ConnectionFailure: return "ConnectionFailure"; case ErrorCode::InvalidJson: return "InvalidJson"; case ErrorCode::DatabaseFailure: return "DatabaseFailure"; case ErrorCode::HttpError: return "HttpError"; case ErrorCode::FileSystemError: return "FileSystemError"; case ErrorCode::FailedToEncrypt: return "FailedToEncrypt"; case ErrorCode::FailedToDecrypt: return "FailedToDecrypt"; case ErrorCode::EndOfFile: return "EndOfFile"; case ErrorCode::FailedToOpenFileForRead: return "FailedToOpenFileForRead"; case ErrorCode::FailedToOpenFileForWrite: return "FailedToOpenFileForWrite"; case ErrorCode::FailedToLaunchProcess: return "FailedToLaunchProcess"; case ErrorCode::Last: return "Last"; default: return "Last"; } } ErrorCode FromString(const std::string &errorCode) { if ("OK" == errorCode) { return ErrorCode::OK; } else if("OutOfSpace" == errorCode) { return ErrorCode::OutOfSpace; } else if("ConnectionFailure" == errorCode) { return ErrorCode::ConnectionFailure; } else if("InvalidJson" == errorCode) { return ErrorCode::InvalidJson; } else if("DatabaseFailure" == errorCode) { return ErrorCode::DatabaseFailure; } else if("HttpError" == errorCode) { return ErrorCode::HttpError; } else if("FileSystemError" == errorCode) { return ErrorCode::FileSystemError; } else if("FailedToEncrypt" == errorCode) { return ErrorCode::FailedToEncrypt; } else if("FailedToDecrypt" == errorCode) { return ErrorCode::FailedToDecrypt; } else if("EndOfFile" == errorCode) { return ErrorCode::EndOfFile; } else if("FailedToOpenFileForRead" == errorCode) { return ErrorCode::FailedToOpenFileForRead; } else if("FailedToOpenFileForWrite" == errorCode) { return ErrorCode::FailedToOpenFileForWrite; } else if("FailedToLaunchProcess" == errorCode) { return ErrorCode::FailedToLaunchProcess; } else if("Last" == errorCode) { return ErrorCode::Last; } return ErrorCode::Last; } }


Bueno, otra opción más. Un caso de uso típico es donde necesita constante para los verbos HTTP, así como el uso de valores de versión de cadena.

El ejemplo:

int main () { VERB a = VERB::GET; VERB b = VERB::GET; VERB c = VERB::POST; VERB d = VERB::PUT; VERB e = VERB::DELETE; std::cout << a.toString() << std::endl; std::cout << a << std::endl; if ( a == VERB::GET ) { std::cout << "yes" << std::endl; } if ( a == b ) { std::cout << "yes" << std::endl; } if ( a != c ) { std::cout << "no" << std::endl; } }

La clase VERBO:

// ----------------------------------------------------------- // ----------------------------------------------------------- class VERB { private: // private constants enum Verb {GET_=0, POST_, PUT_, DELETE_}; // private string values static const std::string theStrings[]; // private value const Verb value; const std::string text; // private constructor VERB (Verb v) : value(v), text (theStrings[v]) { // std::cout << " constructor /n"; } public: operator const char * () const { return text.c_str(); } operator const std::string () const { return text; } const std::string toString () const { return text; } bool operator == (const VERB & other) const { return (*this).value == other.value; } bool operator != (const VERB & other) const { return ! ( (*this) == other); } // --- static const VERB GET; static const VERB POST; static const VERB PUT; static const VERB DELETE; }; const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"}; const VERB VERB::GET = VERB ( VERB::Verb::GET_ ); const VERB VERB::POST = VERB ( VERB::Verb::POST_ ); const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ ); const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ ); // end of file



#include <iostream> #include <map> #define IDMAP(x) (x,#x) std::map<int , std::string> enToStr; class mapEnumtoString { public: mapEnumtoString(){ } mapEnumtoString& operator()(int i,std::string str) { enToStr[i] = str; return *this; } public: std::string operator [] (int i) { return enToStr[i]; } }; mapEnumtoString k; mapEnumtoString& init() { return k; } int main() { init() IDMAP(1) IDMAP(2) IDMAP(3) IDMAP(4) IDMAP(5); std::cout<<enToStr[1]; std::cout<<enToStr[2]; std::cout<<enToStr[3]; std::cout<<enToStr[4]; std::cout<<enToStr[5]; }


#include <stdarg.h> #include <algorithm> #include <string> #include <vector> #include <sstream> #include <map> #define SMART_ENUM(EnumName, ...) / class EnumName / { / private: / static std::map<int, std::string> nameMap; / public: / enum {__VA_ARGS__}; / private: / static std::map<int, std::string> initMap() / { / using namespace std; / / int val = 0; / string buf_1, buf_2, str = #__VA_ARGS__; / replace(str.begin(), str.end(), ''='', '' ''); / stringstream stream(str); / vector<string> strings; / while (getline(stream, buf_1, '','')) / strings.push_back(buf_1); / map<int, string> tmp; / for(vector<string>::iterator it = strings.begin(); / it != strings.end(); / ++it) / { / buf_1.clear(); buf_2.clear(); / stringstream localStream(*it); / localStream>> buf_1 >> buf_2; / if(buf_2.size() > 0) / val = atoi(buf_2.c_str()); / tmp[val++] = buf_1; / } / return tmp; / } / public: / static std::string toString(int aInt) / { / return nameMap[aInt]; / } / }; / std::map<int, std::string> / EnumName::nameMap = EnumName::initMap();

Uso:

SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) cout<<MyEnum::toString(MyEnum::TWO); cout<<MyEnum::toString(10);