Cómo mapear fácilmente enumeraciones de c ++ a cadenas

119

Tengo un montón de tipos de enumeración en algunos archivos de encabezado de biblioteca que estoy usando, y quiero tener una forma de convertir valores de enumeración en 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 montón de funciones como esta, pero creo que eso es 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 el presentimiento de que hay una solución elegante con 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 terceros, por lo que no quiero tener que cambiar la definición de ellas.

Mi instinto ahora es evitar las 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);
Roddy
fuente
1
Posible duplicado de ¿Existe una forma sencilla de convertir la enumeración de C ++ en cadena?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

60

Si desea que los nombres de la enumeración sean cadenas, consulte esta publicación . De lo contrario, std::map<MyEnum, char const*>funcionará bien. (No tiene sentido copiar sus literales de cadena a std :: cadenas en el mapa)

Para obtener más azúcar sintáctico, aquí 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 función template <typename T> map_init(T&)devuelve a map_init_helper<T>. map_init_helper<T>almacena un T & y define lo trivial map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Regresar *thisde operator()permite el encadenamiento de operator(), como operator<<en 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);
}

Dado que la función y la clase de ayuda tienen plantilla, puede usarlas para cualquier mapa o estructura similar a un mapa. Es decir, también puede agregar entradas astd::unordered_map

Si no le gusta escribir estos ayudantes, boost :: assign ofrece la misma funcionalidad lista para usar.

MSalters
fuente
Tiene razón al referirse a otra pregunta. La gente debería echar un vistazo a las "preguntas relacionadas" antes de publicar ...
xtofl
2
@xtofl: ¡Las "preguntas relacionadas" que se muestran aquí son totalmente diferentes a las preguntas relacionadas enumeradas cuando publiqué la pregunta!
Roddy
@MSalters, un std :: map es una forma útil de manejar la implementación, pero estoy buscando algunas formas de reducir el código repetitivo que podría requerir.
Roddy
@MSalters, sería bueno poder aceptar múltiples argumentos para el operador []. pero lamentablemente, no se puede hacer eso. x [a, b] evalúa ax [b]. la expresión (a, b) utiliza el operador de coma. por lo que es equivalente a ["A"] ["B"] ["C"] en su código. podría cambiarlo para decir [eValue1] ["A"] [eValu ..
Johannes Schaub - litb
el operador de llamada de función también sería un buen candidato: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... entonces es 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 solución de MSalters es buena pero básicamente se vuelve a implementar 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;
}
Alastair
fuente
¿Cómo usaría esto cuando eeeToString es un miembro de datos de una clase? Recibo el mensaje "Error: no se permite la inicialización del miembro de datos"
Usuario
@User: los miembros de datos de la clase se inicializan en constructores, generalmente en la lista de inicializadores.
MSalters
¿Hay alguna manera de hacer que esto funcione para todas las enumeraciones? Tengo varias declaraciones de enumeración y no quiero que el mapa solo funcione para el tipo eeeen su caso.
Justin Liang
He intentado utilizar una plantilla, pero luego llegué y error: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang
4
En realidad, esta respuesta es en gran parte obsoleta con C ++ 11.
Alastair
19

Genere automáticamente un formulario a partir de otro.

Fuente:

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

Generado:

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

Si los valores de enumeración 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;
}
jfs
fuente
Si bien es más rápido, no es tan fácil como @MSalters.
kenny
2
Si tiene un poco de perl / python para leer una lista de cadenas de un archivo de texto y generar un archivo .h con el carácter estático en tiempo de compilación. = "Escribir programas para escribir programas"
Martin Beckett
@mgb: perl / python no son las únicas opciones que hará casi cualquier motor de plantilla en cualquier idioma (en este caso, uno está generando ambos formularios a partir de una plantilla).
jfs
@jf. Sí, el punto importante era crear tablas de datos estáticos en tiempo de compilación automáticamente. Probablemente preferiría simplemente generar una matriz estática tonta.
Martin Beckett
¿Funcionará si no se conoce el estado en el momento de la compilación? Estoy bastante seguro de que no lo hará; en teoría, el compilador tendría que crear una instancia de la plantilla enum2str con todos los valores posibles de la enumeración, lo que estoy bastante seguro de que gcc (al menos) no lo hará.
Alastair
11

Recuerdo haber respondido esto en otra parte de StackOverflow. Repitiéndolo aquí. Básicamente es una solución basada en macros variadic 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 su código, simplemente haga:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;
Debdatta Basu
fuente
1
Simplemente cambie la declaración de la clase enum a enum para que funcione en pre c ++ 11.
Debdatta Basu
1
Tienes razón, funciona (el auto también es solo c ++ 11). ¡Buena solución! Sería perfecto si también pudiera establecer un valor para algunas enumeraciones
jamk
Supongo que vi en boost algo así
Sergei
10

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

Para pedir prestado a 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)
David Allan Finch
fuente
¿Hay alguna manera de hacer que la definición de matriz de cadenas de enumeración sea genérica? (No sé cómo manejar una X-Macro dentro de una macro y no manejo la plantilla fácilmente)
Jonathan
5

Utilizo esta 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;
}
Juan González Burgos
fuente
1
Estas son macros X básicas, ¡y estoy asombrado de que esta sea la primera respuesta aquí para sugerirlo! +1
Lightness Races in Orbit
4

Si desea obtener representaciones de cadenas de MyEnum variables , 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 quiere, intente:

#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 cometió en cuestión: case VAL1está duplicado.

Constantin
fuente
En realidad, el nombre del método () no es necesario. Mira mi respuesta.
jfs
3

He pasado más tiempo investigando este tema del que me gustaría admitir. Afortunadamente, existen excelentes soluciones de código abierto en la naturaleza.

Estos son dos grandes enfoques, incluso si no son lo suficientemente conocidos (todavía),

sabio_enum

  • Biblioteca de enumeración inteligente independiente para C ++ 14/11/17. Es compatible con todas las funciones estándar que esperaría de una clase de enumeración inteligente en C ++.
  • Limitaciones: requiere al menos C ++ 11.

Mejores enumeraciones

  • Biblioteca de enumeración reflexiva en tiempo de compilación con sintaxis limpia, en un solo archivo de encabezado y sin dependencias.
  • Limitaciones: basado en macros, no se puede usar dentro de una clase.
jose.angel.jimenez
fuente
2

Me sentiría tentado a tener un mapa m e incrustarlo en la enumeración.

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

y todo está hecho.

Richard Harrison
fuente
2

He requerido esta funcionalidad varias veces para depurar / analizar código de otros. Para esto, escribí un script en Perl que genera una clase con varios toStringmétodos sobrecargados . Cada toStringmétodo toma un Enumcomo argumento y devuelve const char*.

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

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

Valentin Heinitz
fuente
2

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 de enumeración en un archivo separado que luego se #incluye, 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 quiero poder asignar cada nombre de enumeración a la cadena correspondiente (para no afectar el código heredado)

  4. la búsqueda debe ser rápida, por lo que preferiblemente sin cambiar mayúsculas y minúsculas, para esas enormes enumeraciones

Este código crea una enumeración clásica con algunos valores. Además, crea como std :: map que asigna cada valor de enumeración 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 siempre que necesite hacer esto, usará las macros de él:

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

Salud.

muqker
fuente
2

Aquí hay un intento de obtener << y >> operadores de flujo en enumeración automáticamente con un comando de macro de una línea solamente ...

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 de las limitaciones de este esquema ... ¡los comentarios son bienvenidos!

OlivierB
fuente
1

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 un índice de matriz incorrecto. :) Pero puede agregar fácilmente una función para verificar la enumeración antes de obtener la cadena de la matriz.

moogs
fuente
De hecho, una solución que no es para MANCHAS SECAS.
xtofl
ahora que mencionas DRY. el archivo .hy .cpp generado automáticamente a partir de algún otro archivo de entrada. Me encantaría ver mejores soluciones (que no recurran a una complejidad innecesaria)
moogs
1

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

---- EDITAR ----

Después de algunas investigaciones en Internet y algunos experimentos 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 pueda encontrar útil esta solución. No hay necesidad de clases de plantillas, no se necesita c ++ 11 y no se necesita impulso, por lo que esto también podría usarse para C.

---- EDIT2 ----

la tabla de información puede producir algunos problemas al usar 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)};
jamk
fuente
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!",
};

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 de los límites (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

Código aquí: https://github.com/arunksaha/enum_to_string

Madwyn
fuente
1

Sé que llego tarde a la fiesta, pero para todos los que vengan a visitar esta página, pueden probar esto, es más fácil que todo lo que hay allí y tiene más sentido:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}
uIM7AI9S
fuente
¿Sugieres usar cadenas y no usar enumeraciones en absoluto? Eso realmente no resuelve el problema.
Roddy
0

Recientemente tuve el mismo problema con una biblioteca de proveedor (Fincad). Afortunadamente, el proveedor proporcionó documentación 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 le permite interceptar una búsqueda fuera del rango de la enumeración.

Estoy seguro de que swig podría hacer algo similar por usted, pero me complace proporcionar las utilidades de generación de código que están escritas en ruby.

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 quiere ir al revés (enumeración a cadena, en lugar de cadena a enumeración), pero esto debería ser trivial para revertir.

-Pizca


fuente
1
a) ¿Alguien más encuentra esto absolutamente ilegible? Unas pocas definiciones de tipo y el uso de declaraciones mejorarían enormemente la legibilidad. b) las declaraciones estáticas locales no son seguras para subprocesos. c) use const string & en lugar de char *, d) ¿qué hay de incluir el valor que no se pudo encontrar en la excepción lanzada?
Alastair
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 es así, es posible que desee consultar este artículo:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


fuente
0

este viejo lío correcto es mi esfuerzo basado en fragmentos de SO. For_each tendría que expandirse para admitir más de 20 valores de enumeración. Lo probé en visual studio 2019, clang y 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)
);

que produce el siguiente código

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

Es una pena los aros que tienes que saltar con el preprocesador para hacer esto en uno de los lenguajes de programación más utilizados en el mundo ...

rmawatson
fuente
0

Al usar inicializadores de matriz designados, su matriz de cadenas es independiente del orden de los elementos en la enumeración:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Daniel
fuente