Started rework of Error/Report/Panic interface.

Added Option<T>/std::optional<T> interop overloads.

Added Option<T> & Box<T> {static,dynamic}_cast() helper functions.

Fortune for libexopt's current commit: Middle blessing − 中吉
boxed_is_boxed_value
Avril 1 year ago
parent d4e45000bd
commit 1f92834108
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -60,12 +60,52 @@ namespace exopt::types { namespace boxed {
} return *this;
}
[[gnu::nonnull(1)]]
constexpr static Box<T> from_raw_ptr(T* ptr) noexcept {
return Box<T>{ std::unique_ptr<T>{ ptr } };
}
constexpr static Box<T> from_raw_ptr(std::unique_ptr<T>&& uniq)
#if DEBUG
{ if(__builtin_expect(!uniq, false)) throw ptr::NullException{};
#else
noexcept {
_EO_ASSUME(uniq.get() != nullptr);
#endif
return Box<T>{ std::move(uniq) };
}
[[gnu::returns_nonnull]]
constexpr T* get() const noexcept { return m_ptr.get(); }
[[gnu::returns_nonnull]]
constexpr T* release() noexcept { return m_ptr.release(); }
constexpr std::unique_ptr<T>&& into_unique() &&noexcept { return std::move(m_ptr); }
//TODO: Accessors, `(explicit?) operator std::unique_ptr<T> const&[&]`?, etc.
constexpr ~Box() = default;
private:
constexpr explicit Box(std::unique_ptr<T>&& ptr)
: m_ptr(std::move(ptr)) {
_EO_ASSUME(m_ptr.get());
}
std::unique_ptr<T> m_ptr; // Unique<T> m_ptr;
};
template<typename T, typename U> requires(requires(T&& o) { static_cast<U&&>(o); })
constexpr Box<U> static_box_cast(Box<T>&& b) noexcept { return Box<U>::from_raw_ptr(static_cast<U*>(b.release())); }
template<typename T, typename U> requires(requires(T&& o) { static_cast<U&&>(o); })
constexpr Box<U> dynamic_box_cast(Box<T>&& b) {
auto* rel = b.release();
try {
return Box<U>::from_raw_ptr(std::addressof(dynamic_cast<U&&>(std::move(*rel))));
} catch(...) {
delete rel;
throw;
}
} //TODO: Overload for `const&` that does the type check *before* the copy allocation.
}
using boxed::Box;
}

@ -247,6 +247,11 @@ namespace exopt::types {
/// Manually lifetime managed Cow<T>
using either::Cow;
/// Wither an owned T, or a live, mutable reference to T.
template<typename T>
using MaybedOwnedRef = Cow<std::reference_wrapper<T>>; //XXX: Will we have to specialise Cow<> to make this work? \
TODO: I need to re-deisgn it to use a trait like `Borrow` and `ToOwned`, and implement the `owned_type` and `reference_type` to be specialised differently for different types without having to specialise the enture Cow<> struct.
/// RC-lifetime managed Cow<T>
template<typename T>
using RcCow = Cow<std::shared_ptr<T>>;

@ -7,6 +7,7 @@
#include "exopt.h"
#include "optional.h"
#include "either.h"
#include "util.hh"
@ -31,9 +32,9 @@ namespace exopt {
constexpr virtual std::string_view message() const noexcept =0;
constexpr virtual MaybeOwnedRef<Error> inner() noexcept { return {}; } //TODO: Maybe just use `std::optional<std::reference_wrapper<Error>>`
constexpr virtual MaybeOwned<Report> into_report() noexcept { /* TODO */ } // XXX: ^^
constexpr virtual MaybeOwned<Report> into_report() && noexcept { /* TODO: auto{*this}.into_report() */ }
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::MaybeOwned<Report> into_report() && noexcept { /* TODO: auto{*this}.into_report() */ }
constexpr virtual ~Error() = default;
protected:

@ -1,6 +1,6 @@
#pragma once
#include <optional>
#include <utility>
#include <concepts>
@ -21,6 +21,18 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr static inline auto sentinel = nullptr;
};
template<typename T>
struct null_optimise<std::reference_wrapper<T> > { constexpr static inline bool value = true;
using held_type = T*;
using type = std::reference_wrapper<T>;
constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t<type> t) noexcept { return std::addressof(t.get()); }
constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t<held_type> t) noexcept { return std::ref<T>(*t); }
constexpr static inline auto sentinel = nullptr;
};
template<typename T>
struct null_optimise<ptr::Unique<T> > { constexpr static inline bool value = true;
using held_type = T __restrict__*;
@ -233,6 +245,7 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr ~none_t() noexcept = default;
constexpr none_t(std::nullptr_t) noexcept : none_t() {}
constexpr none_t(std::nullopt_t) noexcept : none_t() {}
constexpr operator std::nullptr_t() const noexcept { return nullptr; }
};
@ -268,6 +281,14 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
constexpr static inline bool is_null_optimised = has_null_opt<T>;
constexpr friend void swap(Option& a, Option& b) noexcept {
using std::swap;
swap(a.inner_, b.inner_); //XXX: Swap the values if possible?
}
constexpr Option(std::optional<T>&& opt) noexcept requires(!std::is_void_v<T>)
: inner_( bool(opt) ? std::move(*opt) : nullptr ) {}
constexpr explicit Option(std::nullptr_t) noexcept : inner_(nullptr) {}
constexpr Option(none_t) noexcept : Option(nullptr) {}
constexpr ~Option() noexcept(std::is_nothrow_destructible_v<T>) {}
@ -541,6 +562,24 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
// ---
};
template<typename T, typename U> requires(requires(T&& p) { static_cast<U&&>(p); })
constexpr Option<U> static_opt_cast(Option<T>&& p) noexcept
{
if(bool(p)) return Option<U> { static_cast<U&&>(std::move(*p)) };
return Option<U> { nullptr };
}
template<typename T, typename U> requires(requires(T&& p) { static_cast<U&&>(p); })
constexpr Option<U> dynamic_opt_cast(Option<T>&& p)
{
if(T* ptr = p.try_get_value())
if(U* pv = dynamic_cast<U*>(ptr))
return Option<U> { std::move(*pv) };
return Option<U> { nullptr };
}
static_assert(sizeof(Option<void*>) > sizeof(void*), "Option<T*>: Bad null_opt size");
static_assert(alignof(Option<void*>) == alignof(void*), "Option<T*>: Bad null_opt align");
static_assert(sizeof(Option<ptr::NonNull<int>>) == sizeof(int*), "Option<NonNull<T>>: Bad null_opt size");
@ -557,4 +596,5 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] {
}
using optional::Option;
}

@ -129,10 +129,10 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
{ m_uninit = std::move(c.m_uninit); return *this; }
constexpr T& operator=(T&& value) noexcept {
m_init = std::move(value);
m_init = std::move(value); return *this;
}
constexpr T& operator=(T const& copy) noexcept {
m_init = maybe_uninit{copy};
m_init = maybe_uninit{copy}; return *this; //XXX: These assignment operators may not be a good idea, we can't guarantee which active destriminant it is.
}
constexpr T& assume_init() & noexcept = delete;
@ -153,7 +153,15 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] {
constexpr ~maybe_uninit() noexcept {}
constexpr void assume_init_drop() noexcept(std::is_nothrow_destructible_v<T>)
{ m_init.~T(); }
{ m_init.~T(); m_uninit = {}; }
[[nodiscard]]
constexpr T assume_init_take() noexcept(std::is_nothrow_move_constructible_v<T>)
{
auto v = std::move(m_init);
assume_init_drop();
return v;
}
constexpr maybe_uninit<T> init(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>)
{ return maybe_uninit<T>{std::move(value), EXPLICIT_INIT_TAG<EXPLICIT_INIT_MOVE>{}}; }

Loading…
Cancel
Save