Began rework of Error/TracedError interface.

Removed propagating errors. `Error` is now *the* base class for *all* exopt exceptions.

Fortune for libexopt's current commit: Curse − 凶
error-handling
Avril 1 year ago
parent b80da442b7
commit 044006aef0
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -39,6 +39,15 @@ namespace exopt::types { namespace boxed {
struct Box {
typedef boxable_value_type<T>::type type;
template<std::derived_from<T> U, bool RuntimeCheck=false>
constexpr static Box<T> new_dynamic(U&& v) noexcept(!RuntimeCheck) {
if constexpr(!RuntimeCheck) {
auto boxed = std::make_unique<U>(std::move(v));
return Box<T>{std::unique_ptr<T>{static_cast<T*>(boxed.release())}};
} else
return Box<T>{std::make_unique<T>(dynamic_cast<T&&>(std::move(v)))};
}
constexpr Box() noexcept(std::is_nothrow_default_constructible_v<T>) requires(std::is_default_constructible_v<T>)
: m_ptr{std::make_unique<T>()} {}

@ -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<TracedError> into_traced(std::stacktrace trace&&) &&noexcept;// = std::stacktrace::current()) && noexcept;
inline typed::Box<TracedError> 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<Error&> inner() noexcept { return { std::nullopt }; }
constexpr virtual types::MaybeOwned<Report> into_report() noexcept { /* TODO */ } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`? Or Box<Report&>?
//constexpr virtual types::Option<Error&> inner() noexcept { return { std::nullopt }; }
constexpr virtual types::Option<Error const&> inner() const noexcept { return { std::nullopt }; }
constexpr virtual types::MaybeOwned<Report> into_report() & noexcept { /* TODO */ } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`? Or Box<Report&>?
constexpr virtual types::MaybeOwned<Report> 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<TracedError> into_traced(std::stacktrace trace&&) &&noexcept override final// = std::stacktrace::current()) && noexcept;
{ return typed::Box<TracedError> { 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<TracedError> into_traced(std::stacktrace trace = std::stacktrace::current()) && noexcept override final
{ return typed::Box<TracedError> { 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
}

@ -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)))

@ -220,6 +220,11 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
template<typename T>
using mut_ref_t = typename mut_ref_or_void<T>::type;
template<typename T>
using remove_ref_keep_const_t = std::conditional_t,
<std::is_const_v<std::remove_reference_v<T> >
,const std::remove_cvref_t<T>
, std::remove_cvref_t<T> >;
template<typename T> requires(requires(T v) { { v == nullptr } -> std::convertible_to<bool>; })
[[gnu::always_inline]]
@ -268,7 +273,27 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
template<AsOption T>
struct Option final {
class Option final {
using value_type_correct = _details::remove_ref_keep_const_t<T>;// std::remove_cvref_t<T>;
constexpr static inline bool is_const_v = std::is_const_v<value_type_correct>;
template<typename U, typename V = const std::remove_const_t<U> >
using correct_const_t = std::conditional_t<is_const_v, V, U>;
public:
[[gnu::const]]
constexpr bool is_mutable() const noexcept { return !is_const_v; }
using value_type = std::remove_const_t<value_type_correct>;
using pointer_type = correct_const_t<value_type*, value_type const*>;
using const_pointer_type = value_type const*;
using reference_type = correct_const_t<value_type*, value_type const&>;
using const_reference_type = value_type const&;
using move_reference_type = std::add_rvalue_reference_t<value_type_correct>;
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<T>&& opt) noexcept requires(!std::is_void_v<T>)
: 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<std::add_rvalue_reference_t<T>, Option<T>>) Option(_details::rv_ref_t<T> value) noexcept(std::is_nothrow_invocable_v<convert_to_held_rv, std::add_rvalue_reference_t<T>>) requires(!std::is_void_v<T>)
constexpr explicit(std::convertible_to<std::add_rvalue_reference_t<T>, Option<T>>)
Option(_details::rv_ref_t<T> value)
noexcept(std::is_nothrow_invocable_v<convert_to_held_rv, std::add_rvalue_reference_t<T>>)
requires(!std::is_void_v<T>)
: inner_(convert_to_held_rv(std::forward<decltype(value)>(value)))
{}
constexpr explicit Option(pointer_type const&& ptr) noexcept(std::is_nothrow_move_constructible_v<decltype(*ptr)>) requires(!std::is_void_v<T>)
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<decltype(*ptr)>) requires(!std::is_void_v<T>)
: Option(nullptr) {
if(ptr) inner_ = decltype(inner_)(convert_to_held_rv(auto(*ptr)));
}
template<typename P> requires(!std::is_void_v<T> && (std::is_same_v<std::remove_cvref_t<P>, pointer_type>
or std::is_same_v<std::remove_cvref_t<P>, const_pointer_type>))
constexpr Option<T> from_pointer(P ptr) noexcept
{ if constexpr(std::is_const_v<decltype(*ptr)> or !std::is_rvalue_reference_v<P>)
return Option<T> { static_cast<const_pointer_type>(ptr) };
else return Option<T> { std::move(static_cast<pointer_type>(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<T> || std::is_nothrow_copy_constructible_v<decltype(copy.inner_)::held_type>)

@ -0,0 +1,30 @@
#include <stdexcept>
#include <error.hh>
using namespace exopt::types;
namespace exopt {
Box<TracedError> 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<Error const&> inner() const noexcept override { return Option<Error const&>(*m_error); }//{ return Option<Error const&>::from_pointer(std::as_const(m_error.get())); }
MaybeOwned<Report> into_report() && noexcept override { return std::move(*m_error).into_report(); }
MaybeOwned<Report> into_report() & noexcept override { return m_error->into_report(); }
private:
Box<Error> m_error;
};
#define ERR DynamicTracedError{std::move(*this), std::move(trace)}
return Box<TracedError>::template new_dynamic<DynamicTracedError, false>(ERR);
#undef ERR
}
}
Loading…
Cancel
Save