#pragma once #ifndef MIJIN_UTIL_EXCEPTION_HPP_INCLUDED #define MIJIN_UTIL_EXCEPTION_HPP_INCLUDED 1 #include #include #include #include "../detect.hpp" #include "../debug/stacktrace.hpp" namespace mijin { // // public defines // // // public types // class Exception : public std::runtime_error { private: Result stacktrace_; std::exception_ptr cause_; public: Exception(const std::string& what) : std::runtime_error(what), stacktrace_(captureStacktrace(1)), cause_(std::current_exception()) {} Exception(const char* what) : Exception(std::string(what)) {} [[nodiscard]] const Result& getStacktrace() const noexcept { return stacktrace_; } [[nodiscard]] const std::exception_ptr& getCause() const noexcept { return cause_; } }; // // public functions // template inline decltype(auto) ensure(TCondition&& condition, TExceptionArgs&&... args) { if (!static_cast(std::forward(condition))) { throw TException(std::forward(args)...); } return std::forward(condition); } template void walkExceptionCause(const std::exception_ptr& cause, TFunc func) { if (cause) { try { std::rethrow_exception(cause); } catch(Exception& exc) { func(exc); walkExceptionCause(exc.getCause(), std::move(func)); } catch(std::exception& exc) { func(exc); } catch(...) { func(nullptr); } } } template void walkException(const std::exception& exc, TFunc func) { func(exc); } template void walkException(const mijin::Exception& exc, TFunc func) { func(exc); walkExceptionCause(exc.getCause(), func); } } // namespace mijin template struct std::formatter { using char_t = TChar; template constexpr TContext::iterator parse(TContext& ctx) { auto it = ctx.begin(); auto end = ctx.end(); if (it != end && *it != MIJIN_SMART_QUOTE(char_t, '}')) { throw std::format_error("invalid format"); } return it; } template TContext::iterator format(const mijin::Exception& exception, TContext& ctx) const { using namespace std::literals; auto it = ctx.out(); bool first = true; mijin::walkException(exception, [&](const T& exc) { if constexpr (!std::is_same_v) { if (!first) { it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, "\nCaused by:\n"sv), it).out; } first = false; #if MIJIN_RTTI it = std::ranges::copy(std::basic_string_view(typeid(exc).name()), it).out; it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, ": "sv), it).out; #endif it = std::ranges::copy(std::basic_string_view(exc.what()), it).out; if constexpr (std::is_same_v) { if (const mijin::Result& trace = exc.getStacktrace(); trace.isSuccess()) { *it = MIJIN_SMART_QUOTE(char_t, '\n'); ++it; it = std::format_to(it, MIJIN_SMART_QUOTE(char_t, "{}"), trace.getValue()); } } } else { it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, ""sv), it).out; } }); return it; } }; #endif // MIJIN_UTIL_EXCEPTION_HPP_INCLUDED