Como mapear facilmente enums c ++ para strings


119

Eu tenho um monte de tipos de enum em alguns arquivos de cabeçalho de biblioteca que estou usando e quero ter uma maneira de converter valores de enum em strings de usuário - e vice-versa.

O RTTI não fará isso para mim, porque as 'strings do usuário' precisam ser um pouco mais legíveis do que as enumerações.

Uma solução de força bruta seria um monte de funções como essa, mas acho que é um pouco parecido com 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");
  }
}

Tenho a sensação de que existe uma solução elegante usando modelos, mas ainda não consigo entendê-la.

ATUALIZAÇÃO: obrigado pelas sugestões - eu deveria ter deixado claro que os enums são definidos em um cabeçalho de biblioteca de terceiros, então não quero ter que mudar a definição deles.

Minha intuição agora é evitar modelos e fazer algo assim:

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

Respostas:


60

Se você quiser que o enum se nomeie como strings, consulte esta postagem . Caso contrário, um std::map<MyEnum, char const*>funcionará bem. (Não adianta copiar seus literais de string para std :: strings no mapa)

Para obter mais açúcar sintático, veja como escrever uma classe map_init. O objetivo é permitir

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

A função template <typename T> map_init(T&)retorna um map_init_helper<T>. map_init_helper<T>armazena um T & e define o trivial map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Retornar *thisde operator()permite o encadeamento de operator(), como operator<<em 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);
}

Como a função e a classe auxiliar são modeladas, você pode usá-las para qualquer mapa ou estrutura semelhante a um mapa. Ou seja, também pode adicionar entradas astd::unordered_map

Se você não gosta de escrever esses helpers, boost :: assign oferece a mesma funcionalidade fora da caixa.


Você está certo em se referir a outra questão. As pessoas devem dar uma olhada nas "questões relacionadas" antes de postar ...
xtofl

2
@xtofl: As "questões relacionadas" mostradas aqui são totalmente diferentes das questões relacionadas listadas quando eu postei a questão!
Roddy,

@MSalters, um std :: map é uma maneira útil de lidar com a implementação, mas estou procurando algumas maneiras de reduzir o código clichê que pode exigir.
Roddy,

@MSalters, seria bom aceitar vários argumentos para o operador []. mas, infelizmente, não se pode fazer isso. x [a, b] avalia a x [b]. a expressão (a, b) faz uso do operador vírgula. portanto, é equivalente a ["A"] ["B"] ["C"] em seu código. você poderia alterá-lo para dizer [eValue1] ["A"] [eValu ..
Johannes Schaub - litb

o operador de chamada de função também seria um bom candidato: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... então é 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

A solução MSalters é boa, mas basicamente reimplementada boost::assign::map_list_of. Se você tiver impulso, pode usá-lo diretamente:

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

Como você usaria isso onde eeeToString fosse um membro de dados de uma classe? Estou recebendo "Erro: a inicialização do membro de dados não é permitida"
Usuário

@User: membros de dados de classe são inicializados em construtores, geralmente na lista de inicializadores.
MSalters

Existe uma maneira de fazer isso funcionar para todos os enums. Eu tenho várias declarações enum e não quero que o mapa funcione apenas para o tipo eeeno seu caso.
Justin Liang

Eu tentei usar um modelo, mas depois tem e erro: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang

4
Na verdade, essa resposta está obsoleta com o C ++ 11.
Alastair

19

Gere automaticamente um formulário a partir de outro.

Fonte:

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

Gerado:

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

Se os valores enum forem grandes, um formulário gerado pode usar unordered_map <> ou modelos conforme sugerido por Constantin.

Fonte:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Gerado:

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

Exemplo:

#include <iostream>

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

Embora seja mais rápido, não é tão fácil quanto @MSalters.
Kenny

2
É se você tiver um pouco de perl / python para ler uma lista de strings de um arquivo de texto e gerar um arquivo .h com o char estático em tempo de compilação. = "Escrever programas para escrever programas"
Martin Beckett

@mgb: perl / python não são as únicas opções que quase qualquer mecanismo de template em qualquer linguagem fará (neste caso, um é gerar ambos os formulários a partir de um template).
jfs

@jf. Sim, o ponto importante era construir tabelas de dados estáticos em tempo de compilação automaticamente. Eu provavelmente preferiria apenas gerar uma matriz estática burra.
Martin Beckett

Isso funcionará se o estado não for conhecido no momento da compilação? Tenho quase certeza que não - em teoria, o compilador teria que instanciar o modelo enum2str com todos os valores possíveis do enum, o que tenho quase certeza de que o gcc (pelo menos) não fará.
Alastair

11

Lembro-me de ter respondido isso em outro lugar no StackOverflow. Repetindo aqui. Basicamente, é uma solução baseada em macros variáveis ​​e é muito 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 usá-lo em seu código, basta fazer:

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

1
Basta alterar a declaração da classe enum para enum para funcionar no pré c ++ 11.
Debdatta Basu

1
Você está certo, ele funciona (o auto também é apenas c ++ 11). Ótima solução! Seria perfeito se você também pudesse definir um valor para alguns enums
jamk

Acho que vi no boost algo assim
Sergei

10

Sugiro que uma combinação de macros X são a melhor solução e as seguintes funções de modelo:

Para pedir emprestado marcinkoziukmyopenidcom e estendido

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)

Existe uma maneira de tornar genérica a definição de matriz de string enum? (Não sei como lidar com um X-Macro dentro de uma macro e não manuseio o modelo facilmente)
Jonathan

5

Eu uso esta solução que reproduzo abaixo:

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

1
Estas são macros X básicas, e estou surpreso que esta seja a primeira resposta aqui a sugerir isso! +1
Corridas de Leveza em Órbita de

4

Se você deseja obter representações de strings de MyEnum variáveis , os modelos não vão funcionar. O modelo pode ser especializado em valores integrais conhecidos em tempo de compilação.

No entanto, se é isso que você deseja, tente:

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

Isso é detalhado, mas detectará erros como o que você cometeu - o seu case VAL1está duplicado.


Na verdade, o nome do método () não é necessário. Veja minha resposta.
jfs

3

Passei mais tempo pesquisando esse tópico que gostaria de admitir. Felizmente, existem ótimas soluções de código aberto à solta.

Estas são duas grandes abordagens, mesmo que não sejam bem conhecidas o suficiente (ainda),

sábio_enum

  • Biblioteca enum inteligente independente para C ++ 11/14/17. Ele oferece suporte a todas as funcionalidades padrão que você esperaria de uma classe enum inteligente em C ++.
  • Limitações: requer pelo menos C ++ 11.

Melhores Enums

  • Biblioteca de enum de tempo de compilação reflexiva com sintaxe limpa, em um único arquivo de cabeçalho e sem dependências.
  • Limitações: baseado em macros, não pode ser usado dentro de uma classe.

2

Eu ficaria tentado a ter um mapa m - e embuti-lo no enum.

configurar com m [MyEnum.VAL1] = "Valor 1";

e tudo está feito.


2

Eu exigi essa funcionalidade várias vezes para depurar / analisar código de outros. Para isso, escrevi um script Perl que gera uma classe com vários toStringmétodos sobrecarregados . Cada toStringmétodo recebe um Enumcomo argumento e retorna const char*.

Obviamente, o script não analisa C ++ para enums em si, mas usa ctags para gerar a tabela de símbolos.

O script Perl está aqui: http://heinitz-it.de/download/enum2string/enum2string.pl.html


2

Suas respostas me inspiraram a escrever algumas macros sozinho. Meus requisitos eram os seguintes:

  1. escreva cada valor do enum apenas uma vez, então não há listas duplas para manter

  2. não mantenha os valores enum em um arquivo separado que será #incluído posteriormente, para que eu possa escrever onde quiser

  3. não substitua o enum em si, ainda quero ter o tipo de enum definido, mas, além disso, quero ser capaz de mapear cada nome de enum para a string correspondente (para não afetar o código legado)

  4. a pesquisa deve ser rápida, de preferência sem caixa de troca, para aqueles enormes enums

Este código cria um enum clássico com alguns valores. Além disso, ele cria como std :: map que mapeia cada valor enum para seu nome (ou seja, map [E_SUNDAY] = "E_SUNDAY", etc.)

Ok, aqui está o código agora:

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 é o arquivo que você deseja incluir sempre que precisar fazer isso, você usará as macros dele:

#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 é um exemplo de como usá-lo para criar um enum personalizado:

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

Felicidades.


2

Aqui está uma tentativa de obter operadores de fluxo << e >> no enum automaticamente com um comando de macro de uma linha apenas ...

Definições:

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

Não tenho certeza sobre as limitações deste esquema ... comentários são bem-vindos!


1

no cabeçalho:

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

no arquivo .cpp:

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

Advertência: não lide com índices de array ruins. :) Mas você pode adicionar facilmente uma função para verificar o enum antes de obter a string do array.


Na verdade, uma solução muito não-DRY-SPOT.
xtofl

agora que você mencionou DRY. os arquivos .h e .cpp gerados automaticamente a partir de algum outro arquivo de entrada. Adoraria ver soluções melhores (que não recorressem a complexidade desnecessária)
moogs

1

Eu só queria mostrar essa possível solução elegante usando macros. Isso não resolve o problema, mas acho que é uma boa maneira de repensar o 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 ----

Depois de algumas pesquisas na Internet e algumas experiências próprias, cheguei à seguinte solução:

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

Eu só queria postar talvez alguém possa achar esta solução útil. Não há necessidade de classes de modelos, não há necessidade de c ++ 11 e nem de aumento, então isso também pode ser usado para C.

---- EDIT2 ----

a tabela de informações pode produzir alguns problemas ao usar mais de 2 enums (problema do compilador). A seguinte solução alternativa funcionou:

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

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

Acima está minha solução simples. Um benefício disso é o 'NUM' que controla o tamanho do array da mensagem, ele também impede o acesso fora do limite (se você usá-lo com sabedoria).

Você também pode definir uma função para obter a string:

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

Na sequência da minha solução, achei a seguinte bastante interessante. Geralmente resolvia o problema de sincronização do acima.

Slides aqui: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Codifique aqui: https://github.com/arunksaha/enum_to_string


1

Sei que estou atrasado para a festa, mas para todos os que vierem visitar esta página, você poderia tentar isso, é mais fácil do que tudo lá e faz mais sentido:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}

Você está sugerindo usar strings e não usar enums? Isso realmente não resolve o problema.
Roddy

0

Recentemente, tive o mesmo problema com uma biblioteca de fornecedores (Fincad). Felizmente, o fornecedor forneceu documentação xml para todos os enums. Acabei gerando um mapa para cada tipo de enum e fornecendo uma função de pesquisa para cada enum. Essa técnica também permite interceptar uma pesquisa fora do intervalo do enum.

Tenho certeza que o swig poderia fazer algo semelhante por você, mas estou feliz em fornecer os utilitários de geração de código que são escritos em ruby.

Aqui está um exemplo do 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 você quer seguir o outro caminho (enum para string, em vez de string para enum), mas isso deve ser trivial para reverter.

-Whit


1
a) Alguém mais acha isso absolutamente ilegível? Alguns typedefs e o uso de declarações melhorariam muito a legibilidade. b) as declarações estáticas locais não são threadsafe. c) use const string & em vez de char *, d) que tal incluir o valor que não pôde ser encontrado na exceção lançada?
Alastair

0

Veja se a seguinte sintaxe se adapta a você:

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

Em caso afirmativo, você pode querer verificar este artigo:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


0

esta velha bagunça é meu esforço baseado em bits e peças do SO. O for_each teria que ser expandido para suportar mais de 20 valores enum. Testado no Visual Studio 2019, clang e 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 produz o seguinte 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; 
}

É uma pena os obstáculos que você tem que pular com o pré-processador para fazer isso em uma das linguagens de programação mais usadas no mundo ...


0

Ao usar inicializadores de matriz designados, sua matriz de string é independente da ordem dos elementos no enum:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.