Existe uma maneira simples de converter enum C ++ para string?

Suponha que temos algumas enumerações nomeadas:

enum MyEnum { FOO, BAR = 0x50 }; 

O que eu pesquisei é um script (qualquer idioma) que analisa todos os headers no meu projeto e gera um header com uma function por enumeração.

 char* enum_to_string(MyEnum t); 

E uma implementação com algo parecido com isto:

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

A pegadinha é realmente com enums typedefed e enums de estilo C sem nome. Alguém sabe algo para isso?

EDIT: A solução não deve modificar minha fonte, exceto para as funções geradas. As enums estão em uma API, portanto, usar as soluções propostas até agora não é uma opção.

Você pode querer verificar o GCCXML .

Executar o GCCXML no seu código de exemplo produz:

          

Você pode usar qualquer idioma que preferir para extrair as tags Enumeration e EnumValue e gerar o código desejado.

As macros-X são a melhor solução. Exemplo:

 #include  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) 

No entanto, eu geralmente prefiro o seguinte método, para que seja possível ajustar um pouco a string.

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

@hydroo: Sem o arquivo extra:

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

O que eu costumo fazer é criar uma matriz C com os nomes na mesma ordem e posição que os valores enum.

por exemplo.

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

então você pode usar o array em lugares onde você quer um valor legível, por exemplo

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

Você poderia experimentar um pouco com o operador de stringing (veja # em sua referência de pré-processador) que fará o que quiser, em algumas circunstâncias - por exemplo:

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

irá imprimir "vermelho" para stdout. Infelizmente, não funcionará para uma variável (como você verá o nome da variável impresso)

Eu tenho um incrivelmente simples de usar macro que faz isso de uma forma completamente seca. Envolve macros variadicas e algumas magias simples de análise. Aqui vai:

 #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 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(value)]; \ return os;} 

Para usar isso no seu código, simplesmente faça:

 AWESOME_MAKE_ENUM(Animal, DOG, CAT, HORSE ); 

QT é capaz de puxar isso (graças ao compilador de object meta): link

Eu apenas reinventei esta roda hoje, e pensei em compartilhá-la.

Esta implementação não requer nenhuma mudança no código que define as constantes, que podem ser enumerações ou #define s ou qualquer outra coisa que seja devolvida a um inteiro – no meu caso, eu tinha símbolos definidos em termos de outros símbolos. Também funciona bem com valores esparsos. Até permite múltiplos nomes para o mesmo valor, retornando o primeiro sempre. A única desvantagem é que exige que você faça uma tabela das constantes, que podem ficar desatualizadas à medida que novas são adicionadas, por exemplo.

 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 const char * IdToName(int id, IdAndName (&table)[N]) { return IdToName(id, &table[0], &table[N]); } 

Um exemplo de como você o usa:

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

A function IdToName depende de std::lower_bound para fazer pesquisas rápidas, o que requer que a tabela seja classificada. Se as duas primeiras inputs da tabela estiverem fora de ordem, a function classificará automaticamente.

Edit: Um comentário me fez pensar em outra maneira de usar o mesmo princípio. Uma macro simplifica a geração de uma grande declaração switch .

 #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; } } 
 #define stringify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... stringify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1 

Discussão adicional sobre este método

Dicas de diretivas de pré-processador para recém-chegados

Interessante ver o número de maneiras. Aqui está um que eu usei há muito tempo atrás:

no arquivo myenummap.h:

 #include  #include  enum test{ one, two, three, five=5, six, seven }; struct mymap : std::map { 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(){}; }; 

em main.cpp

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

Não é const, mas é conveniente.

Aqui está outra maneira que usa os resources do C ++ 11. Isto é const, não herda um container STL e é um pouco mais organizado:

 #include  #include  #include  #include  //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 mapping; auto m = [](test const& e,std::string const& s){return mapping(static_cast(e),s);}; std::vector 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(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) ) << "]"< 
 #include  #include  #include  #include  #include  #include  #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ private: \ static std::map nameMap; \ public: \ enum {__VA_ARGS__}; \ private: \ static std::map initMap() \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ map tmp; \ for(vector::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 \ EnumName::nameMap = EnumName::initMap(); 

Uso:

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

Isso pode ser feito em C ++ 11

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

Outra resposta: em alguns contextos, faz sentido definir sua enumeração em um formato não-código, como um arquivo CSV, YAML ou XML e, em seguida, gerar o código de enumeração C ++ e o código de seqüência de caracteres a partir da definição. Essa abordagem pode ou não ser prática em seu aplicativo, mas é algo para se ter em mente.

Esta é uma modificação na resposta @ user3360260. Tem os seguintes novos resources

  • MyEnum fromString(const string&) suporte
  • compila com o VisualStudio 2012
  • o enum é um tipo real de POD (não apenas declarações const), portanto, você pode atribuí-lo a uma variável.
  • adicionado recurso “intervalo” C ++ (em forma de vetor) para permitir iteração “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; } 

Aqui está o 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& tmp) \ { \ using namespace std; \ \ int val = 0; \ string buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ stringstream stream(str); \ vector strings; \ while (getline(stream, buf_1, ',')) \ strings.push_back(buf_1); \ for(vector::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& 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& nameMap() { \ static std::map nameMap0; \ if (nameMap0.size() ==0) initMap(nameMap0); \ return nameMap0; \ } \ static std::vector allValues() { \ std::vector x{ __VA_ARGS__ }; \ return x; \ } \ bool operator< (const EnumName a) const { return (int)*this < (int)a; } \ }; 

Observe que a conversão toString é uma pesquisa rápida, enquanto a conversão fromString é uma pesquisa linear lenta. Mas as cadeias de caracteres são tão caras de qualquer maneira (e o arquivo associado IO), eu não senti a necessidade de otimizar ou usar um bimap.

A solução macro da Suma é legal. Você não precisa ter duas macro diferentes, no entanto. C ++ irá felizmente include um header duas vezes. Apenas deixe de fora o guarda de inclusão.

Então você teria um foobar.h definindo apenas

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

e você includeia assim:

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

enumfactory.h fará 2 #include ENUMFACTORY_ARGUMENT s. Na primeira rodada, ele expande ENUM como o DECLARE_ENUM de Suma; na segunda rodada ENUM funciona como DEFINE_ENUM .

Você pode include enumfactory.h várias vezes também, contanto que você passe em # define diferentes para ENUMFACTORY_ARGUMENT

Note que sua function de conversão deve, idealmente, estar retornando um const char *.

Se você puder colocar suas enums em seus arquivos de header separados, talvez você possa fazer algo assim com macros (ah, isso vai ser feio):

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

Onde enum_def.h tem:

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

E enum_conv.h tem:

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

E finalmente, colour.h tem:

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

E você pode usar a function de conversão como:

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

Isso é feio, mas é a única maneira (no nível de pré-processador) que permite definir seu enum apenas em um único lugar no código. Seu código não é, portanto, propenso a erros devido a modificações no enum. Sua definição de enum e a function de conversão sempre estarão em sincronia. No entanto, repito, isso é feio 🙂

Eu faço isso com classs de wrapper de enum lado-a-lado separadas que são geradas com macros. Existem várias vantagens:

  • Pode gerá-los para enums que eu não defino (por exemplo: enums de header da plataforma OS)
  • Pode incorporar a verificação de intervalo na class wrapper
  • Pode fazer formatação “mais inteligente” com enums de campo de bits

A desvantagem, é claro, é que preciso duplicar os valores enum nas classs do formatador e não tenho nenhum script para gerá-los. Fora isso, porém, parece funcionar muito bem.

Aqui está um exemplo de um enum da minha base de código, sem o código da estrutura que implementa as macros e os modelos, mas você pode ter a ideia:

 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; 

A ideia então é, em vez de usar o EHelpLocation, você usa SEHelpLocation; tudo funciona da mesma forma, mas você obtém verificação de intervalo e um método ‘Format ()’ na própria variável enum. Se você precisar formatar um valor autônomo, você pode usar CEnumFormatter_EHelpLocation :: FormatEnum (…).

Espero que isso seja útil. Eu percebo que isso também não resolve a questão original sobre um script para realmente gerar a outra class, mas espero que a estrutura ajude alguém tentando resolver o mesmo problema, ou escreva um roteiro.

Aqui uma solução de arquivo único (baseada na resposta elegante de @Marcin:

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

Esta foi a minha solução com o BOOST:

 #include  #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  \ 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 criar enum, declare:

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

Para conversões:

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

Um problema com a resposta 0 é que os valores binários enum não necessariamente começam em 0 e não são necessariamente contíguos.

Quando eu preciso disso, eu geralmente:

  • Puxe a definição enum na minha fonte
  • editá-lo para obter apenas os nomes
  • faça uma macro para mudar o nome para a cláusula case na questão, embora tipicamente em uma linha: case foo: return “foo”;
  • adicione o switch, padrão e outra syntax para torná-lo legal

O seguinte script ruby ​​tenta analisar os headers e constrói as fonts necessárias juntamente com os headers originais.

 #! /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)) } } } 

Usando expressões regulares torna esse “analisador” bastante frágil, pode não ser capaz de manipular seus headers específicos normalmente.

Digamos que você tenha um header toto / ah, contendo definições para enums MyEnum e MyEnum2. O script irá construir:

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

Soluções mais robustas seriam:

  • Crie todas as fonts que definem enums e suas operações de outra fonte. Isso significa que você definirá suas enums em um arquivo XML / YML / qualquer que seja muito mais fácil de analisar que C / C ++.
  • Use um compilador real, como sugerido pela Avdi.
  • Use macros de pré-processamento com ou sem modelos.

É um software inédito, mas parece que BOOST_ENUM, de Frank Laub, caberia na conta. A parte que eu gosto sobre isso é que você pode definir um enum dentro do escopo de uma class que a maioria das enums baseadas em Macro geralmente não permitem que você faça. Ele está localizado no Boost Vault em: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Ele não tem nenhum desenvolvimento desde 2006, então eu não sabe como ele compila com os novos lançamentos do Boost. Procure em libs / test para um exemplo de uso.

Isso é praticamente a única maneira que pode ser feito (um array de string pode funcionar também).

O problema é que, uma vez que um programa C é compilado, o valor binário do enum é tudo o que é usado e o nome desaparece.

Aqui está um programa CLI que escrevi para converter facilmente enums em strings. É fácil de usar, e leva cerca de 5 segundos para ser concluído (incluindo o tempo para acessar o diretório que contém o programa, depois executá-lo, passando para ele o arquivo que contém o enum).

Baixe aqui: http://www.mediafire.com/?nttignoozzz

Tópico de discussão sobre isso aqui: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Execute o programa com o argumento “–help” para obter uma descrição de como usá-lo.

Não muito tempo atrás eu fiz algum truque para ter enums corretamente exibidos no QComboBox e ter a definição de enum e representações de string como uma declaração

 #pragma once #include  namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map 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 struct enumerator; template 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\ : enum_singleton< enumerator >\ { \ 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))))) */ 

Agora você tem enumeration::enum_singleton::instance() capaz de converter enums em strings. Se você replace o kv_storage_t pelo boost::bimap , você também poderá fazer uma conversão para trás. A class base comum para o conversor foi introduzida para armazená-lo no object Qt, porque os objects do Qt não poderiam ser modelos

Aparência anterior

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

No código

 #include  enum FORM { F_NONE = 0, F_BOX, F_CUBE, F_SPHERE, }; 

adicionar linhas

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

Trabalhe bem, se os valores em enum não forem dublicados .

Exemplo de uso

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

e vice versa

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

Aqui está uma tentativa de obter operadores de stream < < e >> automaticamente em enum com um comando de macro de uma linha apenas …

Definições:

 #include  #include  #include  #include  #include  #include  #include  #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 enumStr(name##Str, name##Str + len); \ const std::vector::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!

 #include  #include  #define IDMAP(x) (x,#x) std::map 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<  

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) 

Exemplo:

ErrorCode.hpp

 #pragma once #include  #include  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; } } 

Well, yet another option. A typical use case is where you need constant for the HTTP verbs as well as using is string version values.

The example:

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

The VERB class:

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