#pragma once #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include #include "Utils.hpp" #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 ErrorBase&) = default; ErrorBase& operator=(const ErrorBase&) = delete; virtual const char* what() const noexcept override final { std::string ret = this->msg; const std::string replace = nb::NEWLINE + "\t"; if (trace) { ret += replace + find_and_replace( std::string(trace->what()), nb::NEWLINE, replace ); } const std::string& ret_ref = ret; return ret_ref.c_str(); } static const std::string type; static const ErrorCodeMap ErrorMessages; const unsigned int code; const std::string msg; const std::exception* trace; protected: ErrorBase() = delete; 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_="" ) noexcept : code(code_), msg(msg_), trace{static_cast(trace_ ? new TraceType(*trace_) : nullptr) } { 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." ); static_assert(std::is_base_of::value, "`trace_` must be a pointer to a child object of std::exception." ); } 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; class DefaultError {DefaultError() = default;}; template class Error; template<> class Error; template class Error : public ErrorBase { using Base = ErrorBase; public: template Error(Args... T) : Base(T...) {} using Base::what; using Base::code; using Base::msg; using Base::trace; 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::what; using Base::code; using Base::msg; using Base::trace; static const std::string type; static const ErrorCodeMap ErrorMessages; }; } // namespace nb #endif // _NB_ERROR