Si j'ai une énumération comme celle-ci
enum Errors
{ErrorA=0, ErrorB, ErrorC};
Ensuite, je veux imprimer sur la console
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
mais ce que je veux, c'est le texte "ErrorA", puis-je le faire sans utiliser if / switch?
Et quelle est votre solution pour cela?
enum class
: stackoverflow.com/questions/11421432/…Réponses:
Utilisation de la carte:
#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; }
Utilisation d'un tableau de structures avec recherche linéaire:
#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; }
Utilisation du commutateur / boîtier:
#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; }
la source
#p
est le préprocesseur stringifiant p. Donc , appelerPROCESS_VAL(ErrorA)
Affichera:case(ErrorA): s = "ErrorA"; break;
.enum
valeurs que je pense être un NON-GO . 2) Lorsque je comprends correctement la solution, cela ne fonctionne que pour un seulenum
.Utilisez un tableau ou un vecteur de chaînes avec des valeurs correspondantes:
char *ErrorTypes[] = { "errorA", "errorB", "errorC" }; cout << ErrorTypes[anError];
EDIT: La solution ci-dessus est applicable lorsque l'énumération est contiguë, c'est-à-dire qu'elle commence à 0 et qu'aucune valeur n'est attribuée. Cela fonctionnera parfaitement avec l'énumération de la question.
Pour prouver davantage le cas où enum ne commence pas à partir de 0, utilisez:
cout << ErrorTypes[anError - ErrorA];
la source
Voici un exemple basé sur 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; }
la source
DEFINE_ENUM
me donne l'erreurmultiple definition of `format_ProgramStatus(ProgramStatus)'
lorsque j'essaie de l'utiliser.Vous pouvez utiliser une astuce de pré-processeur plus simple si vous êtes prêt à lister vos
enum
entrées dans un fichier externe./* file: errors.def */ /* syntax: ERROR_DEF(name, value) */ ERROR_DEF(ErrorA, 0x1) ERROR_DEF(ErrorB, 0x2) ERROR_DEF(ErrorC, 0x4)
Ensuite, dans un fichier source, vous traitez le fichier comme un fichier d'inclusion, mais vous définissez ce que vous voulez
ERROR_DEF
faire.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 << "]"; } }
Si vous utilisez un outil de navigation source (comme cscope), vous devrez lui faire part du fichier externe.
la source
Il y a eu une discussion ici qui pourrait aider: Y a - t-il un moyen simple de convertir une énumération C ++ en chaîne?
UPDATE: Ici # un script pour Lua qui crée un opérateur << pour chaque énumération nommée qu'il rencontre. Cela peut nécessiter du travail pour que cela fonctionne pour les cas les moins 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)
Compte tenu de cette entrée:
enum Errors {ErrorA=0, ErrorB, ErrorC}; enum Sec { X=1,Y=X,foo_bar=X+1,Z };
Cela produit:
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)"; }}
C'est donc probablement un début pour vous.
[1] énumérations dans des étendues différentes ou non d'espace de noms, énumérations avec des expressions d'initialisation qui contiennent un komma, etc.
la source
J'utilise un tableau de chaînes chaque fois que je définis une énumération:
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;
la source
C'est un bon moyen,
enum Rank { ACE = 1, DEUCE, TREY, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
Imprimez-le avec un tableau de tableaux de caractères
const char* rank_txt[] = {"Ace", "Deuce", "Trey", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Four", "King" } ;
Comme ça
std::cout << rank_txt[m_rank - 1]
la source
#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; }
la source
Vous pouvez utiliser un conteneur de carte 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;
la source
switch(n) { case XXX: return "XXX"; ... }
? Lequel a une recherche O (1) et n'a pas besoin d'être initialisé? Ou les énumérations changent-elles d'une manière ou d'une autre pendant l'exécution?Pour ce problème, je fais une fonction d'aide comme celle-ci:
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; }
La recherche linéaire est généralement plus efficace que
std::map
pour les petites collections comme celle-ci.la source
Cette solution ne vous oblige pas à utiliser des structures de données ou à créer un fichier différent.
En gros, vous définissez toutes vos valeurs d'énumération dans un #define, puis vous les utilisez dans l'opérateur <<. Très similaire à la réponse de @ jxh.
lien ideone pour l'itération finale: http://ideone.com/hQTKQp
Code complet:
#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; }
Production:
Error: [0]NO_ERROR Error: [1]FILE_NOT_FOUND Error: [2]LABEL_UNINITIALISED
Une bonne chose à faire de cette façon est que vous pouvez également spécifier vos propres messages personnalisés pour chaque erreur si vous pensez en avoir besoin:
#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; }
Production:
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
Si vous aimez rendre vos codes d'erreur / descriptions très descriptifs, vous ne les voudrez peut-être pas dans les versions de production. Les désactiver pour que seule la valeur soit imprimée est facile:
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; } }
Production:
Error: 0 Error: 1 Error: 2
Si tel est le cas, la recherche du numéro d'erreur 525 serait un PITA. Nous pouvons spécifier manuellement les nombres dans l'énumération initiale comme ceci:
#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; } } }
Production:
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
la source
Que dis-tu de ça?
enum class ErrorCodes : int{ InvalidInput = 0 }; std::cout << ((int)error == 0 ? "InvalidInput" : "") << std::endl;
etc ... Je sais que c'est un exemple très artificiel mais je pense qu'il a une application là où c'est applicable et nécessaire et est certainement plus court que d'écrire un script pour cela.
la source
Utilisez le préprocesseur:
#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(); } }
L'avantage de cette approche est que: - elle est toujours simple à comprendre, mais - elle permet diverses visites (pas seulement de la chaîne)
Si vous êtes prêt à abandonner le premier, créez vous-même une macro FOREACH (), puis
#define ERROR_VALUES() (ErrorA, ErrorB, ErrorC)
écrivez vos visiteurs en termes de FOREACH (). Ensuite, essayez de passer une revue de code :).la source
Veuillez jeter un œil à ce post:
enum en chaîne dans C ++ 11 / C ++ 14 / C ++ 17 moderne et futur C ++ 20
drz
la source