152 lines
3.8 KiB
C++
152 lines
3.8 KiB
C++
|
|
#pragma once
|
|
|
|
#ifndef MIJIN_UTIL_EXCEPTION_HPP_INCLUDED
|
|
#define MIJIN_UTIL_EXCEPTION_HPP_INCLUDED 1
|
|
|
|
#include <format>
|
|
#include <stdexcept>
|
|
#include <typeinfo>
|
|
|
|
#include "../detect.hpp"
|
|
#include "../debug/stacktrace.hpp"
|
|
|
|
namespace mijin
|
|
{
|
|
|
|
//
|
|
// public defines
|
|
//
|
|
|
|
//
|
|
// public types
|
|
//
|
|
|
|
class Exception : public std::runtime_error
|
|
{
|
|
private:
|
|
Result<Stacktrace> 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<Stacktrace>& getStacktrace() const noexcept { return stacktrace_; }
|
|
|
|
[[nodiscard]]
|
|
const std::exception_ptr& getCause() const noexcept { return cause_; }
|
|
};
|
|
|
|
//
|
|
// public functions
|
|
//
|
|
|
|
template<typename TCondition, typename TException = std::runtime_error, typename... TExceptionArgs>
|
|
inline decltype(auto) ensure(TCondition&& condition, TExceptionArgs&&... args)
|
|
{
|
|
if (!static_cast<bool>(std::forward<TCondition>(condition)))
|
|
{
|
|
throw TException(std::forward<TExceptionArgs>(args)...);
|
|
}
|
|
return std::forward<TCondition>(condition);
|
|
}
|
|
|
|
template<typename TFunc>
|
|
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<typename TFunc>
|
|
void walkException(const std::exception& exc, TFunc func)
|
|
{
|
|
func(exc);
|
|
}
|
|
|
|
template<typename TFunc>
|
|
void walkException(const mijin::Exception& exc, TFunc func)
|
|
{
|
|
func(exc);
|
|
walkExceptionCause(exc.getCause(), func);
|
|
}
|
|
} // namespace mijin
|
|
|
|
template<typename TChar>
|
|
struct std::formatter<mijin::Exception, TChar>
|
|
{
|
|
using char_t = TChar;
|
|
|
|
template<class TContext>
|
|
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<typename TContext>
|
|
TContext::iterator format(const mijin::Exception& exception, TContext& ctx) const
|
|
{
|
|
using namespace std::literals;
|
|
|
|
auto it = ctx.out();
|
|
bool first = true;
|
|
mijin::walkException(exception, [&]<typename T>(const T& exc)
|
|
{
|
|
if constexpr (!std::is_same_v<T, std::nullptr_t>)
|
|
{
|
|
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<T, mijin::Exception>)
|
|
{
|
|
if (const mijin::Result<mijin::Stacktrace>& 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, "<unknown exception>"sv), it).out;
|
|
}
|
|
});
|
|
return it;
|
|
}
|
|
};
|
|
#endif // MIJIN_UTIL_EXCEPTION_HPP_INCLUDED
|