#pragma once #include #ifndef _NB_ERROR #define _NB_ERROR #include #include #include #include #include "TypeTraits.hpp" #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 using has_type = decltype(T::type); template struct NBErrorWhatString; class ErrorBase_what_str_impl : public std::exception { public: virtual std::string what_str_impl() const = 0; virtual std::wstring what_wstr_impl() const = 0; }; template class ErrorBase : public ErrorBase_what_str_impl { public: ErrorBase(const ErrorBase&) = default; ErrorBase& operator=(const ErrorBase&) = delete; template inline const StringType what_str() const noexcept { return NBErrorWhatString::what(*this); } virtual const char* what() const noexcept override final { const std::string& ret = what_str(); return ret.c_str(); } static const std::string type; static const ErrorCodeMap ErrorMessages; const unsigned int code; const std::string msg; const std::shared_ptr trace; const bool traceIsNBError; 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{trace_ ? std::make_shared(*trace_) : nullptr}, traceIsNBError( trace_ ? is_detected::value : false) { 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_ ) {} std::string what_str_impl() const override final { return what_str(); } std::wstring what_wstr_impl() const override final { return what_str(); } }; template const std::string ErrorBase::type = ErrorType::type; template const ErrorCodeMap ErrorBase::ErrorMessages = ErrorType::ErrorMessages; template struct NBErrorWhatString { inline static const std::wstring what(const ErrorBase& err) noexcept { std::wstring ret = nb::str_to_wstr(err.msg); const std::wstring replace = nb::WNEWLINE + L"┗-"; if (err.trace) { std::wstring trace_msg; if (err.traceIsNBError) { // How can I access trace_msg = std::static_pointer_cast(err.trace)->what_wstr_impl(); } else { trace_msg = nb::str_to_wstr(std::string(err.trace->what())); } trace_msg = find_and_replace(trace_msg, L"┗", L"-"); ret += replace + find_and_replace( trace_msg, nb::WNEWLINE, replace ); } return ret; } }; template struct NBErrorWhatString { inline static const std::string what(const ErrorBase& err) noexcept { std::string ret = err.msg; const std::string replace = nb::NEWLINE+" "; if (err.trace) { ret += replace + find_and_replace( std::string(err.trace->what()), nb::NEWLINE, replace ); } return ret; } }; 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