From 044006aef0d142ad585e3e0d68d7104bc72ba96d Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 2 May 2023 21:59:04 +0100 Subject: [PATCH] Began rework of Error/TracedError interface. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed propagating errors. `Error` is now *the* base class for *all* exopt exceptions. Fortune for libexopt's current commit: Curse − 凶 --- include/boxed.h | 9 ++++++++ include/error.hh | 39 +++++++++++++++++++++++++++++----- include/exopt.h | 32 ++++++++++++++++++++++++++++ include/optional.h | 52 +++++++++++++++++++++++++++++++++++++++++++--- src/error.cpp | 30 ++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 src/error.cpp diff --git a/include/boxed.h b/include/boxed.h index 4ffdfb1..ba7e41c 100644 --- a/include/boxed.h +++ b/include/boxed.h @@ -39,6 +39,15 @@ namespace exopt::types { namespace boxed { struct Box { typedef boxable_value_type::type type; + template U, bool RuntimeCheck=false> + constexpr static Box new_dynamic(U&& v) noexcept(!RuntimeCheck) { + if constexpr(!RuntimeCheck) { + auto boxed = std::make_unique(std::move(v)); + return Box{std::unique_ptr{static_cast(boxed.release())}}; + } else + return Box{std::make_unique(dynamic_cast(std::move(v)))}; + } + constexpr Box() noexcept(std::is_nothrow_default_constructible_v) requires(std::is_default_constructible_v) : m_ptr{std::make_unique()} {} diff --git a/include/error.hh b/include/error.hh index c6113e8..8d7253a 100644 --- a/include/error.hh +++ b/include/error.hh @@ -11,13 +11,24 @@ #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 propagated returned (base, abstract) + + // This is base thrown (base, abstract) class Error { public: constexpr Error(Error const&) = default; @@ -25,6 +36,9 @@ namespace exopt { 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; } @@ -32,8 +46,9 @@ namespace exopt { constexpr virtual std::string_view message() const noexcept =0; - constexpr virtual types::Option inner() noexcept { return { std::nullopt }; } - constexpr virtual types::MaybeOwned into_report() noexcept { /* TODO */ } //TODO: Maybe just use `std::optional>`? Or Box? + //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; @@ -47,7 +62,7 @@ namespace exopt { }; // This is propagated returned, along with a stack trace. (base, abstract) - class TracedError : Error { + class TracedError : public Error { public: TracedError(TracedError const &) = default; TracedError(TracedError &&) = default; @@ -62,13 +77,27 @@ namespace exopt { 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 } diff --git a/include/exopt.h b/include/exopt.h index 7ef0b7a..74b697e 100644 --- a/include/exopt.h +++ b/include/exopt.h @@ -27,11 +27,43 @@ extern "C" { # define _EO_ASSUME(X) ({ if(! (X)) __builtin_unreachable(); }) #endif +#define _EO_NOTHING + #if _EO(cpp) # ifndef _EO_ASSUME # define _EO_ASSUME(X) [[gnu::assume(X)]] # endif # define _exopt__restrict __restrict__ +#define _EO_CTORS_BASIC_W(N, constexpr, noexcept, ...) \ + constexpr N(N const&) noexcept __VA_ARGS__; \ + constexpr N(N &&) noexcept __VA_ARGS__; \ + constexpr N & operator=(N const&) noexcept __VA_ARGS__; \ + constexpr N & operator=(N &&) noexcept __VA_ARGS__ +#define _EO_CTORS_BASIC(...) _EO_CTORS_BASIC_W(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING) +#define _EO_CTORS_BASIC_DEFAULT(...) _EO_CTORS_BASIC(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING, = default) + +#define _EO_CTORS_DEFAULT_BASIC_W(N, constexpr, noexcept, ...) \ + constexpr N(N const&) noexcept = default; \ + constexpr N(N &&) noexcept = default; \ + constexpr N & operator=(N const&) noexcept = default; \ + constexpr N & operator=(N &&) noexcept = default + +#define _EO_CTORS_DEFAULT_BASIC(N) _EO_CTORS_DEFAULT_BASIC_W(N, constexpr, _EO_NOTHING, _EO_NOTHING) +#define _EO_CTORS_DEFAULT_BASIC_RT(N) _EO_CTORS_DEFAULT_BASIC_W(N, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING) +#define _EO_CTORS_DEFAULT_BASIC_NE_W(N, C) _EO_CTORS_DEFAULT_BASIC_W(N, C, noexcept, _EO_NOTHING) +#define _EO_CTORS_DEFAULT_BASIC_NE(N) _EO_CTORS_DEFAULT_BASIC_W(N, constexpr, noexcept, _EO_NOTHING) +#define _EO_CTORS_DEFAULT_BASIC_NE_RT(N) _EO_CTORS_DEFAULT_BASIC_W(N, _EO_NOTHING, noexcept, _EO_NOTHING) + +#define _EO_CLASS_DEFAULT_(N, ...) N +#define _EO_CLASS_DEFAULT_2(_, N, ...) N +#define _EO_CLASS_DEFAULT_3(_, __, N, ...) N +#define _EO_CLASS_DEFAULT_4(_, __, ___, N, ...) N +#define _EO_CLASS_DEFAULT_3REST(_, __, ___, ...) __VA_ARGS__ +#define _EO_CLASS_DEFAULT_4REST(_, __, ___, ____, ...) __VA_ARGS__ +// _EO_CLASS_DEFINE(NAME, [constexpr], [noexcept], [virtual], [=default]) +#define _EO_CLASS_DEFINE(...) _EO_CTORS_BASIC(__VA_ARGS__); \ + _EO_CLASS_DEFAULT_4(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING) _EO_CLASS_DEFAULT_2(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING) ~ _EO_CLASS_DEFAULT_(__VA_ARGS__, _EO_NOTHING)() _EO_CLASS_DEFAULT_3(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING) _EO_CLASS_DEFAULT_4REST(__VA_ARGS__, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING, _EO_NOTHING) + #else # ifndef _EO_ASSUME # define _EO_ASSUME(X) __attribute__((assume(X))) diff --git a/include/optional.h b/include/optional.h index 09da245..de35c54 100644 --- a/include/optional.h +++ b/include/optional.h @@ -220,6 +220,11 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { template using mut_ref_t = typename mut_ref_or_void::type; + template + using remove_ref_keep_const_t = std::conditional_t, + > + ,const std::remove_cvref_t + , std::remove_cvref_t >; template requires(requires(T v) { { v == nullptr } -> std::convertible_to; }) [[gnu::always_inline]] @@ -268,7 +273,27 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { template - struct Option final { + class Option final { + using value_type_correct = _details::remove_ref_keep_const_t;// std::remove_cvref_t; + constexpr static inline bool is_const_v = std::is_const_v; + template > + using correct_const_t = std::conditional_t; + + public: + [[gnu::const]] + constexpr bool is_mutable() const noexcept { return !is_const_v; } + + using value_type = std::remove_const_t; + + using pointer_type = correct_const_t; + using const_pointer_type = value_type const*; + + using reference_type = correct_const_t; + using const_reference_type = value_type const&; + + using move_reference_type = std::add_rvalue_reference_t; + using const_move_reference_type = value_type const&&; + struct UnwrapError : public OptionUnwrapError { UnwrapError() noexcept =default; @@ -287,7 +312,9 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { } constexpr Option(std::optional&& opt) noexcept requires(!std::is_void_v) - : inner_( bool(opt) ? std::move(*opt) : nullptr ) {} + : Option(nullptr) { + if(opt) inner_ = decltype(inner_)(std::move(*opt)); + } constexpr explicit Option(std::nullptr_t) noexcept : inner_(nullptr) {} constexpr Option(none_t) noexcept : Option(nullptr) {} @@ -300,9 +327,28 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { : inner_(std::move(move.inner_)) {} - constexpr explicit(std::convertible_to, Option>) Option(_details::rv_ref_t value) noexcept(std::is_nothrow_invocable_v>) requires(!std::is_void_v) + constexpr explicit(std::convertible_to, Option>) + Option(_details::rv_ref_t value) + noexcept(std::is_nothrow_invocable_v>) + requires(!std::is_void_v) : inner_(convert_to_held_rv(std::forward(value))) {} + constexpr explicit Option(pointer_type const&& ptr) noexcept(std::is_nothrow_move_constructible_v) requires(!std::is_void_v) + Option(nullptr) { + if(ptr) inner_ = decltype(inner_)(convert_to_held_rv(std::move(*ptr))); + } + constexpr explicit Option(const_pointer_type ptr) noexcept(std::is_nothrow_copy_constructible_v) requires(!std::is_void_v) + : Option(nullptr) { + if(ptr) inner_ = decltype(inner_)(convert_to_held_rv(auto(*ptr))); + } + + template requires(!std::is_void_v && (std::is_same_v, pointer_type> + or std::is_same_v, const_pointer_type>)) + constexpr Option from_pointer(P ptr) noexcept + { if constexpr(std::is_const_v or !std::is_rvalue_reference_v

) + return Option { static_cast(ptr) }; + else return Option { std::move(static_cast(ptr)) }; } + //: inner_(ptr ? convert_to_held_rv(*ptr) : nullptr) {} //TODO: copy/move assign where appropriate constexpr Option& operator=(const Option& copy) noexcept(std::is_void_v || std::is_nothrow_copy_constructible_v) diff --git a/src/error.cpp b/src/error.cpp new file mode 100644 index 0000000..59cc41d --- /dev/null +++ b/src/error.cpp @@ -0,0 +1,30 @@ +#include + +#include + +using namespace exopt::types; + +namespace exopt { + Box Error::into_traced(std::stacktrace&& trace) &&noexcept { + struct DynamicTracedError final : TracedError { + _EO_CTORS_BASIC_DEFAULT(DynamicTracedError, _EO_NOTHING, noexcept); + virtual ~DynamicTracedError() noexcept = default; + + DynamicTracedError(Error&& self, std::stacktrace&& trace) noexcept + : TracedError(std::move(trace), std::move(self.m_location)) + , m_error(std::move(self)) {} + + std::string_view message() const noexcept override { return m_error->message(); } + Option inner() const noexcept override { return Option(*m_error); }//{ return Option::from_pointer(std::as_const(m_error.get())); } + MaybeOwned into_report() && noexcept override { return std::move(*m_error).into_report(); } + MaybeOwned into_report() & noexcept override { return m_error->into_report(); } + + + private: + Box m_error; + }; +#define ERR DynamicTracedError{std::move(*this), std::move(trace)} + return Box::template new_dynamic(ERR); +#undef ERR + } +}