#pragma once #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include namespace nb { typedef std::unordered_map ErrorCodeMap; template class ErrorBase : public std::exception { public: static std::string lookup(unsigned int); virtual const char* what() const noexcept override final { return static_cast(this)->_msg.c_str(); }; unsigned int code() const noexcept { return static_cast(this)->_code; }; protected: ErrorBase() = default; ErrorBase( const unsigned int code, std::string msg, std::exception* const trace, unsigned int line=0, std::string filename="" ) noexcept : _code{code} { static_assert(std::is_same, decltype(ErrorType::ErrorMessages)>::value, "const std::unordered_map ErrorMessages must be " "a class member." ); static_assert(std::is_enum_v, "enum ErrorCodes must be a class member."); static_assert(std::is_same, unsigned int>::value, "enum ErrorCodes must be of underlying type unsigned int." ); static_assert(std::is_same::value, "const std::string type must be a class member." ); _msg = std::string(ErrorType::type); _msg += ":" + std::to_string(code); if (line && filename.size()) { _msg += " in \'" + filename + "\':" + std::to_string(line); } _msg += "\n\t" + msg; if (trace) { _msg += "\nTraceback - " + std::string(trace->what()); } } const unsigned int _code; std::string _msg; }; class Error : public ErrorBase { public: Error(unsigned int code, std::exception* const trace=nullptr) : ErrorBase(code, ErrorBase::lookup(code), trace) {} enum ErrorCodes : unsigned int { UNDEFINED, BADERRORCODE }; static const std::string type; static const ErrorCodeMap ErrorMessages; }; template std::string ErrorBase::lookup(unsigned int code) { for (auto kv : ErrorType::ErrorMessages) { if (kv.first==code) { return std::string(kv.second); } } throw Error(Error::ErrorCodes::BADERRORCODE); } } // namespace nb #endif // _NB_ERROR