Если у меня есть такое перечисление
enum Errors
{ErrorA=0, ErrorB, ErrorC};
Затем я хочу распечатать на консоли
Errors anError = ErrorA;
cout<<anError;/// 0 will be printed
но я хочу текст «ErrorA», могу ли я сделать это без использования if / switch?
И каково ваше решение для этого?
enum class
: stackoverflow.com/questions/11421432/…Ответы:
Используя карту:
#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; }
Использование массива структур с линейным поиском:
#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; }
Использование переключателя / корпуса:
#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; }
источник
#p
препроцессор, преобразующий p. Так вызываяPROCESS_VAL(ErrorA)
выход будет:case(ErrorA): s = "ErrorA"; break;
.enum
значений, что, по моему мнению, НЕПРОХОДИТ . 2) Когда я правильно понимаю решение, оно работает только для одногоenum
.Используйте массив или вектор строк с соответствующими значениями:
char *ErrorTypes[] = { "errorA", "errorB", "errorC" }; cout << ErrorTypes[anError];
РЕДАКТИРОВАТЬ: вышеприведенное решение применимо, когда перечисление является непрерывным, то есть начинается с 0 и нет присвоенных значений. Он будет отлично работать с перечислением в вопросе.
Чтобы еще раз доказать это для случая, когда enum не начинается с 0, используйте:
cout << ErrorTypes[anError - ErrorA];
источник
Вот пример, основанный на 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; }
источник
DEFINE_ENUM
выдает ошибку,multiple definition of `format_ProgramStatus(ProgramStatus)'
когда я пытаюсь его использовать.Вы можете использовать более простой прием препроцессора, если хотите перечислить свои
enum
записи во внешнем файле./* file: errors.def */ /* syntax: ERROR_DEF(name, value) */ ERROR_DEF(ErrorA, 0x1) ERROR_DEF(ErrorB, 0x2) ERROR_DEF(ErrorC, 0x4)
Затем в исходном файле вы обрабатываете файл как включаемый файл, но вы определяете, что вы хотите,
ERROR_DEF
чтобы он делал.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 << "]"; } }
Если вы используете какой-либо инструмент для просмотра исходного кода (например, cscope), вам нужно сообщить ему о внешнем файле.
источник
Здесь было обсуждение, которое могло бы помочь: есть ли простой способ преобразовать перечисление C ++ в строку?
ОБНОВЛЕНИЕ: # вот сценарий для Lua, который создает оператор << для каждого встреченного именованного перечисления. Это может потребовать некоторой работы, чтобы заставить его работать в менее простых случаях [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)
Учитывая этот ввод:
enum Errors {ErrorA=0, ErrorB, ErrorC}; enum Sec { X=1,Y=X,foo_bar=X+1,Z };
Производит:
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)"; }}
Так что это, наверное, начало для вас.
[1] перечисления в различных областях или областях, не относящихся к пространству имен, перечисления с выражениями инициализатора, которые содержат комму и т. Д.
источник
Я использую массив строк всякий раз, когда определяю перечисление:
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;
источник
Это хороший способ,
enum Rank { ACE = 1, DEUCE, TREY, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING };
Распечатайте его с массивом символьных массивов
const char* rank_txt[] = {"Ace", "Deuce", "Trey", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Four", "King" } ;
Как это
std::cout << rank_txt[m_rank - 1]
источник
#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; }
источник
Вы можете использовать контейнер stl map ....
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;
источник
switch(n) { case XXX: return "XXX"; ... }
? Что имеет поиск O (1) и не требует инициализации? Или перечисления как-то меняются во время выполнения?Для этой проблемы я использую такую вспомогательную функцию:
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; }
Линейный поиск обычно более эффективен, чем
std::map
для таких небольших коллекций.источник
Это решение не требует от вас использования каких-либо структур данных или создания другого файла.
По сути, вы определяете все свои значения перечисления в #define, а затем используете их в операторе <<. Очень похоже на ответ @ jxh.
ссылка ideone для финальной итерации: http://ideone.com/hQTKQp
Полный код:
#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; }
Выход:
Error: [0]NO_ERROR Error: [1]FILE_NOT_FOUND Error: [2]LABEL_UNINITIALISED
В этом способе хорошо то, что вы также можете указать свои собственные сообщения для каждой ошибки, если считаете, что они вам нужны:
#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; }
Выход:
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
Если вам нравится делать коды / описания ошибок очень информативными, возможно, они вам не понадобятся в производственных сборках. Отключить их так, чтобы печаталось только значение:
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; } }
Выход:
Error: 0 Error: 1 Error: 2
Если это так, то номер ошибки 525 будет PITA. Мы можем вручную указать числа в начальном перечислении следующим образом:
#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; } } }
Выход:
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
источник
Как насчет этого?
enum class ErrorCodes : int{ InvalidInput = 0 }; std::cout << ((int)error == 0 ? "InvalidInput" : "") << std::endl;
и т.д ... Я знаю, что это очень надуманный пример, но я думаю, что он имеет приложение, где это применимо и необходимо, и, безусловно, короче, чем написание сценария для него.
источник
Воспользуйтесь препроцессором:
#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(); } }
Преимущество этого подхода в том, что: - он все еще прост для понимания, но - он допускает различные посещения (не только строки)
Если вы готовы отказаться от первого, создайте макрос FOREACH (), а затем
#define ERROR_VALUES() (ErrorA, ErrorB, ErrorC)
напишите своих посетителей в терминах FOREACH (). Тогда попробуйте пройти проверку кода :).источник
Пожалуйста, взгляните на этот пост:
enum to string в современных C ++ 11 / C ++ 14 / C ++ 17 и будущих C ++ 20
Drz
источник