#pragma once #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include #include "TypeTraits.hpp" #include #include "StringUtils.hpp" namespace nb { typedef std::unordered_map ErrorCodeMap; template using has_type = decltype(T::type); template class ErrorBase; template<> class ErrorBase : public std::exception { public: const unsigned int code; const std::string msg; const std::shared_ptr trace; const bool traceIsNBError; virtual inline std::string str() const noexcept = 0; virtual const char* what() const noexcept override final { const std::string& ret = str(); return ret.c_str(); } protected: template < typename TraceType = std::exception, std::enable_if_t, bool> = true > ErrorBase( const unsigned int& code_, const std::string& msg_, const std::shared_ptr trace_, const unsigned int& line_, std::string filename_ ) noexcept : code(code_), msg(msg_), trace{std::dynamic_pointer_cast(trace_)}, traceIsNBError( trace_ ? is_detected::value : false) { static_assert(std::is_base_of::value, "`trace_` must be a pointer to a child object of std::exception." ); } }; template class ErrorBase : public ErrorBase { using Base = ErrorBase; public: ErrorBase(const ErrorBase&) = default; ErrorBase& operator=(const ErrorBase&) = delete; virtual inline std::string str() const noexcept override { std::string ret = msg + nb::NEWLINE; if (trace) { const std::string tabover = " "; std::string trace_msg; if (traceIsNBError) { trace_msg = std::static_pointer_cast(trace)->str(); trace_msg = std::string(std::string_view(trace_msg).substr( 0, trace_msg.length()-nb::NEWLINE.length() )); } else { trace_msg = std::string(trace->what()); } ret += indent_strblock(trace_msg, tabover, tabover+"Trace: ")+nb::NEWLINE; } return ret; } static const std::string type; static const ErrorCodeMap ErrorMessages; using Base::code; using Base::msg; using Base::trace; using Base::traceIsNBError; protected: using Base::Base; template < typename TraceType = std::exception, std::enable_if_t, bool> = true > ErrorBase( const unsigned int code_, std::string msg_, const TraceType* trace_=nullptr, unsigned int line_=0, std::string filename_="" ) : Base( code_, msg_, trace_ ? std::make_shared(*trace_) : nullptr, line_, filename_ ) { 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." ); } template < typename TraceType, std::enable_if_t, bool> = true > ErrorBase( const unsigned int& code_, const TraceType& trace_, unsigned int line_=0, std::string filename_="" ) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_), &trace_, line_, filename_ ) {} template < typename TraceType, std::enable_if_t, bool> = true > ErrorBase( const std::string& msg_, const TraceType& trace_, unsigned int line_=0, std::string filename_="" ) noexcept : ErrorBase( 0, msg_, &trace_, line_, filename_ ) {} ErrorBase( const unsigned int& code_, unsigned int line_=0, std::string filename_="" ) noexcept : ErrorBase( code_, ErrorType::ErrorMessages.at(code_), static_cast(nullptr), line_, filename_ ) {} ErrorBase( const std::string& msg_, unsigned int line_=0, std::string filename_="" ) noexcept : ErrorBase( 0, msg_, nullptr, line_, filename_ ) {} }; template const std::string ErrorBase::type = ErrorType::type; template const ErrorCodeMap ErrorBase::ErrorMessages = ErrorType::ErrorMessages; template class Error; template<> class Error; template class Error : public ErrorBase { using Base = ErrorBase; public: template Error(Args... T) : Base(T...) {} using Base::str; using Base::what; using Base::code; using Base::msg; using Base::trace; using Base::traceIsNBError; using Base::type; using Base::ErrorMessages; friend ErrorBase; }; template<> class Error : public ErrorBase> { using Base = ErrorBase>; public: using Base::Base; Error() : Base(0) {} enum Codes : unsigned int { GENERAL, UNDEFINED, BADERRORCODE }; using Base::str; 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