#pragma once #include #include #include #include "exopt.h" #include "optional.h" #include "either.h" #include "util.hh" #define _EO_DEFEXCEPT_BASIC_(NAME, BASE, ...) class NAME : public ::exopt:: BASE { \ constexpr static inline auto MESSSAGE = (__VA_ARGS__); \ public: \ _EO_CLASS_DEFINE(NAME, constexpr, noexcept, virtual, =default); \ constexpr virtual std::string_view message() const noexcept override { return { MESSAGE }; } \ } #define _EO_DEFEXCEPT_BASIC(NAME, MSG) _EO_DEFEXCEPT_BASIC_(NAME, Error, MSG) #define _EO_DEFEXCEPT_TRACE_BASIC(NAME, MSG) _EO_DEFEXCEPT_BASIC_(NAME, TracedError, MSG) namespace exopt { // Report generated from an error. (virtual base, not abstract.) class Report { public: virtual ~Report(); }; // This is base thrown (base, abstract) class Error { public: constexpr Error(Error const&) = default; constexpr Error(Error &&) = default; constexpr Error& operator=(Error &&) = default; constexpr Error& operator=(Error const&) = default; virtual types::Box into_traced(std::stacktrace trace&&) &&noexcept;// = std::stacktrace::current()) && noexcept; inline typed::Box into_traced(std::stacktrace trace = std::stacktrace::current()) && noexcept { return std::move(*this).into_traced(std::move(trace)); } constexpr virtual std::source_location const& location() const noexcept { return m_location; } constexpr operator std::string_view() const noexcept { return message(); } constexpr virtual std::string_view message() const noexcept =0; //constexpr virtual types::Option inner() noexcept { return { std::nullopt }; } constexpr virtual types::Option inner() const noexcept { return { std::nullopt }; } constexpr virtual types::MaybeOwned into_report() & noexcept { /* TODO */ } //TODO: Maybe just use `std::optional>`? Or Box? constexpr virtual types::MaybeOwned into_report() && noexcept { /* TODO: auto{*this}.into_report() */ } constexpr virtual ~Error() = default; protected: constexpr Error(std::source_location loc = std::source_location::current()) noexcept : m_location(std::move(loc)) {} private: std::source_location m_location; //Moved this to non-constexpr subclass `TracedError` \ std::unique_ptr m_trace{nullptr}; //TODO: Change to exopt::types::Box<> to make copy ctor/assign work }; // This is propagated returned, along with a stack trace. (base, abstract) class TracedError : public Error { public: TracedError(TracedError const &) = default; TracedError(TracedError &&) = default; TracedError& operator=(TracedError &&) = default; TracedError& operator=(TracedError const&) = default; virtual ~TracedError() = default; inline virtual std::stacktrace const& stacktrace() const noexcept { return m_trace; } protected: inline TracedError( std::source_location loc = std::source_location::current() , std::stacktrace trace = std::stacktrace::current()) noexcept : Error(std::move(loc)), m_trace(std::move(trace)) {} /*inline TracedError( std::stacktrace&& trace, , std::source_location loc = std::source_location::current()) noexcept : Error(std::move(loc)), m_trace(std::move(trace)) {}*/ inline TracedError( std::stacktrace&& trace, , std::source_location&& loc) noexcept : Error(std::move(loc)), m_trace(std::move(trace)) {} private: inline types::Box into_traced(std::stacktrace trace&&) &&noexcept override final// = std::stacktrace::current()) && noexcept; { return typed::Box { std::move(*this) }; } //TODO: XXX: How to check if `this` is *already* boxed? This dynamic double-boxing is inefficient, but also can probably not be checked unless relying on enabling RTTI by default inline typed::Box into_traced(std::stacktrace trace = std::stacktrace::current()) && noexcept override final { return typed::Box { std::move(*this) }; } std::stacktrace m_trace; //TODO: Maybe box this? idk how big it is... }; #if 0 // This is thrown. (virtual base, abstract) class Panic : /*virtual?? I think it should be XXX*/ Error { //TODO: How to make a `constexpr` deduced panic that has stack-trace at runtime only? Is it needed? Hnm... }; //TODO: Fatal(string) : public virtual Panic #endif }