#pragma once #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include #ifndef THROW_WITH_INFO #ifdef CODE_ERROR_LOCATIONS #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) #else #define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__) #endif // CODE_ERROR_LOCATIONS #endif // THROW_WITH_INFO #ifndef THROW #ifdef LOGGING #define THROW_WTIH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__) #else #define THROW(...) THROW_WITH_INFO(__VA_ARGS__) #endif // LOGGING #endif // THROW namespace nb { typedef std::unordered_map ErrorCodeMap; template class ErrorBase : public std::exception { public: ErrorBase( const unsigned int code, unsigned int line=0, std::string filename="" ) noexcept : ErrorBase( code, ErrorBase::lookup(code), line, filename ) {} ErrorBase( const unsigned int code, const std::exception& trace, unsigned int line=0, std::string filename="" ) noexcept : ErrorBase( code, ErrorBase::lookup(code), trace, line, filename ) {} static std::string lookup(unsigned int); unsigned int code() const noexcept { return static_cast(this)->_code; }; virtual const char* what() const noexcept override final { return _msg.c_str(); }; protected: ErrorBase() = default; ErrorBase( const unsigned int code, std::string msg, unsigned int line=0, std::string filename="" ) noexcept : _code{code}, _msg{""} { 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 " + msg; } ErrorBase( const unsigned int code, std::string msg, const std::exception& trace, unsigned int line=0, std::string filename="" ) noexcept : ErrorBase(code, msg, line, filename) { std::string what_msg(trace.what()); std::string::size_type newline_pos=-1; while((newline_pos=what_msg.find("\n", newline_pos+1))!= std::string::npos) { what_msg.replace(newline_pos, 1, "\n "); } _msg += "\n Trace - " + what_msg; } unsigned int _code; std::string _msg; }; class Error : public ErrorBase { public: enum ErrorCodes : unsigned int { GENERAL, UNDEFINED, BADERRORCODE }; using ErrorBase::ErrorBase; friend ErrorBase; protected: 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