To rozwiązanie nie wymaga używania żadnych struktur danych ani tworzenia innego pliku.
Zasadniczo definiujesz wszystkie wartości wyliczenia w #define, a następnie używasz ich w operatorze <<. Bardzo podobne do odpowiedzi @ jxh.
łącze ideone do końcowej iteracji: http://ideone.com/hQTKQp
Pełny kod:
#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:
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;
}
Wynik:
Error: [0]NO_ERROR
Error: [1]FILE_NOT_FOUND
Error: [2]LABEL_UNINITIALISED
Fajną rzeczą w robieniu tego w ten sposób jest to, że możesz również określić własne niestandardowe komunikaty dla każdego błędu, jeśli uważasz, że ich potrzebujesz:
#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;
}
Wynik:
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
Jeśli chcesz, aby kody / opisy błędów były bardzo opisowe, możesz ich nie chcieć w kompilacjach produkcyjnych. Wyłączenie ich, aby drukowana była tylko wartość, jest łatwe:
inline std::ostream& operator<<(std::ostream& os, Error err)
{
int errVal = static_cast<int>(err);
switch (err)
{
#ifndef PRODUCTION_BUILD
#define ERROR_VALUE(NAME,DESCR) case Error::NAME: return os << "[" << errVal << "]" #NAME <<"; " << DESCR;
ERROR_VALUES
#undef ERROR_VALUE
#endif
default:
return os << errVal;
}
}
Wynik:
Error: 0
Error: 1
Error: 2
W takim przypadku znalezienie błędu numer 525 byłoby PITA. Możemy ręcznie określić liczby w początkowym wyliczeniu w następujący sposób:
#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
#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:
{
return os << static_cast<int>(err);
break;
}
}
}
Wynik:
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