#pragma once #ifndef _NB_ERRORS_IMPL #define _NB_ERRORS_IMPL #include #include #include #include #include #include "StringUtils.hpp" #include "TypeTraits.hpp" namespace nb { typedef std::unordered_map ErrorCodeMap; template class Error; class ErrorBase { protected: public: const unsigned int code; const std::string msg; const std::string type; const std::shared_ptr trace; ErrorBase(const ErrorBase&) = default; ErrorBase(const std::exception&) noexcept; virtual std::string what() const noexcept { std::string ret = msg; if (trace) { std::string trace_msg = msg; ret += nb::NEWLINE + indent_strblock( trace_msg, nb::TABOVER, nb::TABOVER+"Trace: " ); } return ret; } protected: ErrorBase( unsigned int code_, std::string msg_, std::string type_, std::shared_ptr trace_ ) noexcept : code(code_), msg(msg_), type(type_), trace{trace_} {} }; template class Error : public ErrorBase { private: void inline check_asserts() { static_assert(std::is_same::value, "const std::unordered_map ErrorMessages must be " "a class member." ); static_assert(std::is_enum_v, "enum Codes must be a class member."); static_assert(std::is_same, unsigned int>::value, "enum Codes must be of underlying type unsigned int." ); static_assert(std::is_same::value, "const std::string type must be a class member." ); } protected: using ErrorBase::ErrorBase; public: Error( unsigned int code_, const ErrorBase& trace_ ) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_), ErrorType::type, std::make_shared(trace_) ) { check_asserts(); } Error( std::string msg_, const ErrorBase& trace_ ) noexcept : ErrorBase( 0, msg_, ErrorType::type, std::make_shared(trace_) ) { check_asserts(); } Error(unsigned int code_) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_), ErrorType::type, nullptr ) { check_asserts(); } Error(std::string msg_) noexcept : ErrorBase( 0, msg_, ErrorType::type, nullptr ) { check_asserts(); } Error( unsigned int code_, const std::string& note_, const ErrorBase& trace_ ) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", ErrorType::type, std::make_shared(trace_) ) { check_asserts(); } Error(unsigned int code_, const std::string& note_) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_) + " (" + note_ + ")", ErrorType::type, nullptr ) { check_asserts(); } using ErrorBase::what; using ErrorBase::code; using ErrorBase::msg; using ErrorBase::trace; using ErrorBase::type; }; template<> class Error : public Error> { using Base = Error>; public: using Base::Base; Error(unsigned int code_=1) noexcept : Base(code_) {} Error(const std::exception& err) : Base( Codes::STANDARD, std::string(err.what()), "std::exception", nullptr ) {} // fix Error(std::string msg_) noexcept : Base( Codes::UNDEFINED, msg_ ) {} enum Codes : unsigned int { STANDARD, UNDEFINED }; using Base::what; using Base::code; using Base::msg; using Base::trace; static const std::string type; static const ErrorCodeMap ErrorMessages; }; } // namespace nb #endif // _NB_ERRORS_IMPL