mijin2/source/mijin/util/exception.hpp
2025-08-30 00:30:47 +02:00

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