#pragma once #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include "TypeTraits.hpp" #include #include "StringUtils.hpp" namespace nb { typedef std::unordered_map ErrorCodeMap; template constexpr bool IsValidException_v = std::is_base_of_v; template using IsValidException = std::enable_if_t, bool>; template class ErrorBase; template class Error; template constexpr bool IsValidNBError_v = std::is_base_of_v, T>; template using IsValidNBError = std::enable_if_t, bool>; template<> class ErrorBase { public: const unsigned int code; const std::string msg; const std::string type; const std::shared_ptr trace; const bool traceIsNBError; virtual std::string what() const noexcept = 0; virtual std::shared_ptr make_shared() const noexcept = 0; protected: template ErrorBase(const ErrorBase& err) : ErrorBase(std::move(err)) {} template ErrorBase(ErrorBase&& err) : ErrorBase( err.code, err.msg, err.type, (err.traceIsNBError) ? static_cast(err.trace.get()) : static_cast(err.trace.get()) ) {} ErrorBase( unsigned int code_, std::string msg_, std::string type_, const std::exception* trace_ ) noexcept : code(code_), msg(msg_), type(type_), trace{ trace_ ? std::static_pointer_cast( std::make_shared>(*trace_)) : nullptr, }, traceIsNBError(false) {} template, bool> = true> ErrorBase( unsigned int code_, std::string msg_, std::string type_, const T* trace_ ) noexcept : code(code_), msg(msg_), type(type_), trace{trace_ ? trace_->make_shared() : nullptr}, traceIsNBError(true) {} }; template class ErrorBase : public ErrorBase { private: using Base = ErrorBase; void inline check_asserts() { 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 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." ); } public: template ErrorBase(const ErrorBase& cpy) : Base(cpy) { check_asserts(); } virtual std::shared_ptr make_shared() const noexcept override { return std::static_pointer_cast( std::make_shared(*this) ); } virtual std::string what() const noexcept override { std::string ret = msg; if (trace) { std::string trace_msg; if (traceIsNBError) { trace_msg = std::static_pointer_cast(trace)->what(); } else { trace_msg = std::string(std::static_pointer_cast(trace)->what()); } ret += nb::NEWLINE + indent_strblock( trace_msg, nb::TABOVER, nb::TABOVER+"Trace: " ); } return ret; } static const std::string type; static const ErrorCodeMap ErrorMessages; using Base::code; using Base::msg; using Base::trace; using Base::traceIsNBError; friend ErrorType; friend Error; protected: using Base::Base; template ErrorBase( unsigned int code_, std::string msg_, std::string type_, const T* trace_ ) : Base( code_, msg_, type_, trace_ ) { check_asserts(); } }; template const std::string ErrorBase::type = ErrorType::type; template const ErrorCodeMap ErrorBase::ErrorMessages = ErrorType::ErrorMessages; template class Error : public ErrorBase { using Base = ErrorBase; public: template Error( unsigned int code_, const T& trace_ ) noexcept : Base( code_, ErrorType::ErrorMessages.at(code_), ErrorType::type, &trace_ ) {} template Error( std::string msg_, const ErrorBase& trace_ ) noexcept : Base( 0, msg_, ErrorType::type, &trace_ ) {} Error( std::string msg_, const std::exception& trace_ ) noexcept : Base( 0, msg_, ErrorType::type, &trace_ ) {} Error(unsigned int code_) noexcept : Base( code_, ErrorType::ErrorMessages.at(code_), ErrorType::type, NULLPTR ) {} Error(std::string msg_) noexcept : Base( 0, msg_, ErrorType::type, NULLPTR ) {} using Base::what; using Base::code; using Base::msg; using Base::trace; using Base::traceIsNBError; using Base::type; using Base::ErrorMessages; protected: using Base::Base; }; template<> class Error : public ErrorBase> { using Base = ErrorBase>; public: using Base::Base; Error(unsigned int code_=1) noexcept : Base( code_, ErrorMessages.at(code_), type, NULLPTR ) {} Error(const std::exception& err) : Base( Codes::STANDARD, std::string(err.what()), "std::exception", nullptr ) {} Error(std::string msg_) noexcept : Base( Codes::UNDEFINED, msg_, type, NULLPTR ) {} enum Codes : unsigned int { STANDARD, UNDEFINED }; using Base::what; using Base::code; using Base::msg; using Base::trace; using Base::traceIsNBError; static const std::string type; static const ErrorCodeMap ErrorMessages; }; } // namespace nb #endif // _NB_ERROR