Se eu tiver um enum como este
enum Errors
{ErrorA=0, ErrorB, ErrorC};
Então eu quero imprimir para o console
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
mas o que eu quero é o texto "ErrorA", posso fazer isso sem usar if / switch?
E qual é a sua solução para isso?
enum class
: stackoverflow.com/questions/11421432/…Respostas:
Usando o mapa:
#include <iostream> #include <map> #include <string> enum Errors {ErrorA=0, ErrorB, ErrorC}; std::ostream& operator<<(std::ostream& out, const Errors value){ static std::map<Errors, std::string> strings; if (strings.size() == 0){ #define INSERT_ELEMENT(p) strings[p] = #p INSERT_ELEMENT(ErrorA); INSERT_ELEMENT(ErrorB); INSERT_ELEMENT(ErrorC); #undef INSERT_ELEMENT } return out << strings[value]; } int main(int argc, char** argv){ std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl; return 0; }
Usando matriz de estruturas com pesquisa linear:
#include <iostream> #include <string> enum Errors {ErrorA=0, ErrorB, ErrorC}; std::ostream& operator<<(std::ostream& out, const Errors value){ #define MAPENTRY(p) {p, #p} const struct MapEntry{ Errors value; const char* str; } entries[] = { MAPENTRY(ErrorA), MAPENTRY(ErrorB), MAPENTRY(ErrorC), {ErrorA, 0}//doesn't matter what is used instead of ErrorA here... }; #undef MAPENTRY const char* s = 0; for (const MapEntry* i = entries; i->str; i++){ if (i->value == value){ s = i->str; break; } } return out << s; } int main(int argc, char** argv){ std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl; return 0; }
Usando switch / case:
#include <iostream> #include <string> enum Errors {ErrorA=0, ErrorB, ErrorC}; std::ostream& operator<<(std::ostream& out, const Errors value){ const char* s = 0; #define PROCESS_VAL(p) case(p): s = #p; break; switch(value){ PROCESS_VAL(ErrorA); PROCESS_VAL(ErrorB); PROCESS_VAL(ErrorC); } #undef PROCESS_VAL return out << s; } int main(int argc, char** argv){ std::cout << ErrorA << std::endl << ErrorB << std::endl << ErrorC << std::endl; return 0; }
fonte
#p
é o pré-processador que restringe p. Então, chamandoPROCESS_VAL(ErrorA)
saída será:case(ErrorA): s = "ErrorA"; break;
.enum
valores, o que considero NÃO-GO . 2) Quando eu entendo a solução corretamente, ela funciona apenas para umenum
.Use uma matriz ou vetor de strings com valores correspondentes:
char *ErrorTypes[] = { "errorA", "errorB", "errorC" }; cout << ErrorTypes[anError];
EDIT: A solução acima é aplicável quando o enum é contíguo, ou seja, começa em 0 e não há valores atribuídos. Funcionará perfeitamente com o enum na questão.
Para comprovar ainda mais o caso em que enum não começa de 0, use:
cout << ErrorTypes[anError - ErrorA];
fonte
Aqui está um exemplo baseado em Boost.Preprocessor:
#include <iostream> #include <boost/preprocessor/punctuation/comma.hpp> #include <boost/preprocessor/control/iif.hpp> #include <boost/preprocessor/comparison/equal.hpp> #include <boost/preprocessor/stringize.hpp> #include <boost/preprocessor/seq/for_each.hpp> #include <boost/preprocessor/seq/size.hpp> #include <boost/preprocessor/seq/seq.hpp> #define DEFINE_ENUM(name, values) \ enum name { \ BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_VALUE, , values) \ }; \ inline const char* format_##name(name val) { \ switch (val) { \ BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_FORMAT, , values) \ default: \ return 0; \ } \ } #define DEFINE_ENUM_VALUE(r, data, elem) \ BOOST_PP_SEQ_HEAD(elem) \ BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(elem), 2), \ = BOOST_PP_SEQ_TAIL(elem), ) \ BOOST_PP_COMMA() #define DEFINE_ENUM_FORMAT(r, data, elem) \ case BOOST_PP_SEQ_HEAD(elem): \ return BOOST_PP_STRINGIZE(BOOST_PP_SEQ_HEAD(elem)); DEFINE_ENUM(Errors, ((ErrorA)(0)) ((ErrorB)) ((ErrorC))) int main() { std::cout << format_Errors(ErrorB) << std::endl; }
fonte
DEFINE_ENUM
me dá o erromultiple definition of `format_ProgramStatus(ProgramStatus)'
quando tento usá-la.Você pode usar um truque mais simples do pré-processador se quiser listar suas
enum
entradas em um arquivo externo./* file: errors.def */ /* syntax: ERROR_DEF(name, value) */ ERROR_DEF(ErrorA, 0x1) ERROR_DEF(ErrorB, 0x2) ERROR_DEF(ErrorC, 0x4)
Então, em um arquivo de origem, você trata o arquivo como um arquivo de inclusão, mas define o que deseja
ERROR_DEF
fazer.enum Errors { #define ERROR_DEF(x,y) x = y, #include "errors.def" #undef ERROR_DEF }; static inline std::ostream & operator << (std::ostream &o, Errors e) { switch (e) { #define ERROR_DEF(x,y) case y: return o << #x"[" << y << "]"; #include "errors.def" #undef ERROR_DEF default: return o << "unknown[" << e << "]"; } }
Se você usar alguma ferramenta de navegação de fonte (como cscope), você terá que informá-la sobre o arquivo externo.
fonte
Houve uma discussão aqui que pode ajudar: Existe uma maneira simples de converter enum C ++ em string?
ATUALIZAÇÃO: Aqui # um script para Lua que cria um operador << para cada enum nomeado que encontra. Isso pode exigir algum trabalho para que funcione nos casos menos simples [1]:
function make_enum_printers(s) for n,body in string.gmatch(s,'enum%s+([%w_]+)%s*(%b{})') do print('ostream& operator<<(ostream &o,'..n..' n) { switch(n){') for k in string.gmatch(body,"([%w_]+)[^,]*") do print(' case '..k..': return o<<"'..k..'";') end print(' default: return o<<"(invalid value)"; }}') end end local f=io.open(arg[1],"r") local s=f:read('*a') make_enum_printers(s)
Dada esta entrada:
enum Errors {ErrorA=0, ErrorB, ErrorC}; enum Sec { X=1,Y=X,foo_bar=X+1,Z };
Produz:
ostream& operator<<(ostream &o,Errors n) { switch(n){ case ErrorA: return o<<"ErrorA"; case ErrorB: return o<<"ErrorB"; case ErrorC: return o<<"ErrorC"; default: return o<<"(invalid value)"; }} ostream& operator<<(ostream &o,Sec n) { switch(n){ case X: return o<<"X"; case Y: return o<<"Y"; case foo_bar: return o<<"foo_bar"; case Z: return o<<"Z"; default: return o<<"(invalid value)"; }}
Portanto, é provavelmente um começo para você.
[1] enums em escopos diferentes ou não de namespace, enums com expressões inicializadoras que contêm um komma etc.
fonte
Eu uso uma matriz de string sempre que defino um enum:
Profile.h
#pragma once struct Profile { enum Value { Profile1, Profile2, }; struct StringValueImplementation { const wchar_t* operator[](const Profile::Value profile) { switch (profile) { case Profile::Profile1: return L"Profile1"; case Profile::Profile2: return L"Profile2"; default: ASSERT(false); return NULL; } } }; static StringValueImplementation StringValue; };
Profile.cpp
#include "Profile.h" Profile::StringValueImplementation Profile::StringValue;
fonte
Esta é uma boa maneira,
enum Rank { ACE = 1, DEUCE, TREY, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
Imprima-o com uma série de matrizes de caracteres
const char* rank_txt[] = {"Ace", "Deuce", "Trey", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Four", "King" } ;
Como isso
std::cout << rank_txt[m_rank - 1]
fonte
#include <iostream> using std::cout; using std::endl; enum TEnum { EOne, ETwo, EThree, ELast }; #define VAR_NAME_HELPER(name) #name #define VAR_NAME(x) VAR_NAME_HELPER(x) #define CHECK_STATE_STR(x) case(x):return VAR_NAME(x); const char *State2Str(const TEnum state) { switch(state) { CHECK_STATE_STR(EOne); CHECK_STATE_STR(ETwo); CHECK_STATE_STR(EThree); CHECK_STATE_STR(ELast); default: return "Invalid"; } } int main() { int myInt=12345; cout << VAR_NAME(EOne) " " << VAR_NAME(myInt) << endl; for(int i = -1; i < 5; i) cout << i << " " << State2Str((TEnum)i) << endl; return 0; }
fonte
Você pode usar um contêiner de mapa stl ....
typedef map<Errors, string> ErrorMap; ErrorMap m; m.insert(ErrorMap::value_type(ErrorA, "ErrorA")); m.insert(ErrorMap::value_type(ErrorB, "ErrorB")); m.insert(ErrorMap::value_type(ErrorC, "ErrorC")); Errors error = ErrorA; cout << m[error] << endl;
fonte
switch(n) { case XXX: return "XXX"; ... }
? Qual tem pesquisa O (1) e não precisa ser inicializado? Ou os enums mudam de alguma forma durante o tempo de execução?Para este problema, faço uma função de ajuda como esta:
const char* name(Id id) { struct Entry { Id id; const char* name; }; static const Entry entries[] = { { ErrorA, "ErrorA" }, { ErrorB, "ErrorB" }, { 0, 0 } } for (int it = 0; it < gui::SiCount; ++it) { if (entries[it].id == id) { return entries[it].name; } } return 0; }
A pesquisa linear geralmente é mais eficiente do que
std::map
para pequenas coleções como esta.fonte
Esta solução não requer que você use nenhuma estrutura de dados ou crie um arquivo diferente.
Basicamente, você define todos os seus valores enum em um #define e, em seguida, os usa no operador <<. Muito semelhante à resposta de @jxh.
link ideone para iteração final: http://ideone.com/hQTKQp
Código completo:
#include <iostream> #define ERROR_VALUES ERROR_VALUE(NO_ERROR)\ ERROR_VALUE(FILE_NOT_FOUND)\ ERROR_VALUE(LABEL_UNINITIALISED) enum class Error { #define ERROR_VALUE(NAME) NAME, ERROR_VALUES #undef ERROR_VALUE }; inline std::ostream& operator<<(std::ostream& os, Error err) { int errVal = static_cast<int>(err); switch (err) { #define ERROR_VALUE(NAME) case Error::NAME: return os << "[" << errVal << "]" #NAME; ERROR_VALUES #undef ERROR_VALUE default: // If the error value isn't found (shouldn't happen) return os << errVal; } } int main() { std::cout << "Error: " << Error::NO_ERROR << std::endl; std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl; std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl; return 0; }
Resultado:
Error: [0]NO_ERROR Error: [1]FILE_NOT_FOUND Error: [2]LABEL_UNINITIALISED
Uma coisa boa sobre fazer dessa maneira é que você também pode especificar suas próprias mensagens personalizadas para cada erro, se achar que precisa delas:
#include <iostream> #define ERROR_VALUES ERROR_VALUE(NO_ERROR, "Everything is fine")\ ERROR_VALUE(FILE_NOT_FOUND, "File is not found")\ ERROR_VALUE(LABEL_UNINITIALISED, "A component tried to the label before it was initialised") enum class Error { #define ERROR_VALUE(NAME,DESCR) NAME, ERROR_VALUES #undef ERROR_VALUE }; inline std::ostream& operator<<(std::ostream& os, Error err) { int errVal = static_cast<int>(err); switch (err) { #define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR; ERROR_VALUES #undef ERROR_VALUE default: return os << errVal; } } int main() { std::cout << "Error: " << Error::NO_ERROR << std::endl; std::cout << "Error: " << Error::FILE_NOT_FOUND << std::endl; std::cout << "Error: " << Error::LABEL_UNINITIALISED << std::endl; return 0; }
Resultado:
Error: [0]NO_ERROR; Everything is fine Error: [1]FILE_NOT_FOUND; File is not found Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised
Se você gosta de tornar seus códigos / descrições de erro muito descritivos, talvez não os queira em compilações de produção. Desativá-los para que apenas o valor seja impresso é fácil:
inline std::ostream& operator<<(std::ostream& os, Error err) { int errVal = static_cast<int>(err); switch (err) { #ifndef PRODUCTION_BUILD // Don't print out names in production builds #define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR; ERROR_VALUES #undef ERROR_VALUE #endif default: return os << errVal; } }
Resultado:
Error: 0 Error: 1 Error: 2
Se for esse o caso, encontrar o erro número 525 seria um PITA. Podemos especificar manualmente os números no enum inicial assim:
#define ERROR_VALUES ERROR_VALUE(NO_ERROR, 0, "Everything is fine")\ ERROR_VALUE(FILE_NOT_FOUND, 1, "File is not found")\ ERROR_VALUE(LABEL_UNINITIALISED, 2, "A component tried to the label before it was initialised")\ ERROR_VALUE(UKNOWN_ERROR, -1, "Uh oh") enum class Error { #define ERROR_VALUE(NAME,VALUE,DESCR) NAME=VALUE, ERROR_VALUES #undef ERROR_VALUE }; inline std::ostream& operator<<(std::ostream& os, Error err) { int errVal = static_cast<int>(err); switch (err) { #ifndef PRODUCTION_BUILD // Don't print out names in production builds #define ERROR_VALUE(NAME,VALUE,DESCR) case Error::NAME: return os << "[" #VALUE "]" #NAME <<"; " << DESCR; ERROR_VALUES #undef ERROR_VALUE #endif default: return os <<errVal; } } ERROR_VALUES #undef ERROR_VALUE #endif default: { // If the error value isn't found (shouldn't happen) return os << static_cast<int>(err); break; } } }
Resultado:
Error: [0]NO_ERROR; Everything is fine Error: [1]FILE_NOT_FOUND; File is not found Error: [2]LABEL_UNINITIALISED; A component tried to the label before it was initialised Error: [-1]UKNOWN_ERROR; Uh oh
fonte
Que tal agora?
enum class ErrorCodes : int{ InvalidInput = 0 }; std::cout << ((int)error == 0 ? "InvalidInput" : "") << std::endl;
etc ... Eu sei que este é um exemplo altamente elaborado, mas acho que tem aplicação onde aplicável e necessário e é certamente mais curto do que escrever um script para ele.
fonte
Use o pré-processador:
#define VISIT_ERROR(FIRST, MIDDLE, LAST) \ FIRST(ErrorA) MIDDLE(ErrorB) /* MIDDLE(ErrorB2) */ LAST(ErrorC) enum Errors { #define ENUMFIRST_ERROR(E) E=0, #define ENUMMIDDLE_ERROR(E) E, #define ENUMLAST_ERROR(E) E VISIT_ERROR(ENUMFIRST_ERROR, ENUMMIDDLE_ERROR, ENUMLAST_ERROR) // you might undefine the 3 macros defined above }; std::string toString(Error e) { switch(e) { #define CASERETURN_ERROR(E) case E: return #E; VISIT_ERROR(CASERETURN_ERROR, CASERETURN_ERROR, CASERETURN_ERROR) // you might undefine the above macro. // note that this will produce compile-time error for synonyms in enum; // handle those, if you have any, in a distinct macro default: throw my_favourite_exception(); } }
A vantagem desta abordagem é que: - ainda é simples de entender - permite várias visitas (não apenas string)
Se você estiver disposto a descartar o primeiro, crie uma macro FOREACH () e, em seguida,
#define ERROR_VALUES() (ErrorA, ErrorB, ErrorC)
escreva seus visitantes em termos de FOREACH (). Em seguida, tente passar por uma revisão de código :).fonte
Por favor, dê uma olhada nesta postagem:
enum para string em C ++ 11 / C ++ 14 / C ++ 17 moderno e futuro C ++ 20
drz
fonte