diff --git a/include/boxed.h b/include/boxed.h index 4f65d33..4ffdfb1 100644 --- a/include/boxed.h +++ b/include/boxed.h @@ -60,12 +60,52 @@ namespace exopt::types { namespace boxed { } return *this; } + [[gnu::nonnull(1)]] + constexpr static Box from_raw_ptr(T* ptr) noexcept { + return Box{ std::unique_ptr{ ptr } }; + } + constexpr static Box from_raw_ptr(std::unique_ptr&& uniq) +#if DEBUG + { if(__builtin_expect(!uniq, false)) throw ptr::NullException{}; +#else + + noexcept { + _EO_ASSUME(uniq.get() != nullptr); +#endif + return Box{ 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&& into_unique() &&noexcept { return std::move(m_ptr); } + //TODO: Accessors, `(explicit?) operator std::unique_ptr const&[&]`?, etc. constexpr ~Box() = default; private: + constexpr explicit Box(std::unique_ptr&& ptr) + : m_ptr(std::move(ptr)) { + _EO_ASSUME(m_ptr.get()); + } + std::unique_ptr m_ptr; // Unique m_ptr; }; + + template requires(requires(T&& o) { static_cast(o); }) + constexpr Box static_box_cast(Box&& b) noexcept { return Box::from_raw_ptr(static_cast(b.release())); } + + template requires(requires(T&& o) { static_cast(o); }) + constexpr Box dynamic_box_cast(Box&& b) { + auto* rel = b.release(); + try { + return Box::from_raw_ptr(std::addressof(dynamic_cast(std::move(*rel)))); + } catch(...) { + delete rel; + throw; + } + } //TODO: Overload for `const&` that does the type check *before* the copy allocation. } using boxed::Box; } diff --git a/include/either.hh b/include/either.hh index b582c59..169fe6c 100644 --- a/include/either.hh +++ b/include/either.hh @@ -247,6 +247,11 @@ namespace exopt::types { /// Manually lifetime managed Cow using either::Cow; + /// Wither an owned T, or a live, mutable reference to T. + template + using MaybedOwnedRef = Cow>; //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 template using RcCow = Cow>; diff --git a/include/error.hh b/include/error.hh index d6f1f22..c6113e8 100644 --- a/include/error.hh +++ b/include/error.hh @@ -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 inner() noexcept { return {}; } //TODO: Maybe just use `std::optional>` - constexpr virtual MaybeOwned into_report() noexcept { /* TODO */ } // XXX: ^^ - constexpr virtual MaybeOwned into_report() && noexcept { /* TODO: auto{*this}.into_report() */ } + 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::MaybeOwned into_report() && noexcept { /* TODO: auto{*this}.into_report() */ } constexpr virtual ~Error() = default; protected: diff --git a/include/optional.h b/include/optional.h index 4cb464a..09da245 100644 --- a/include/optional.h +++ b/include/optional.h @@ -1,6 +1,6 @@ #pragma once - +#include #include #include @@ -21,6 +21,18 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { constexpr static inline auto sentinel = nullptr; }; + template + struct null_optimise > { constexpr static inline bool value = true; + using held_type = T*; + using type = std::reference_wrapper; + constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t t) noexcept { return std::addressof(t.get()); } + constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t t) noexcept { return std::ref(*t); } + + constexpr static inline auto sentinel = nullptr; + }; + + + template struct null_optimise > { 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; + 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&& opt) noexcept requires(!std::is_void_v) + : 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) {} @@ -541,6 +562,24 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { // --- }; + template requires(requires(T&& p) { static_cast(p); }) + constexpr Option static_opt_cast(Option&& p) noexcept + { + if(bool(p)) return Option { static_cast(std::move(*p)) }; + return Option { nullptr }; + } + + template requires(requires(T&& p) { static_cast(p); }) + constexpr Option dynamic_opt_cast(Option&& p) + { + if(T* ptr = p.try_get_value()) + if(U* pv = dynamic_cast(ptr)) + return Option { std::move(*pv) }; + + return Option { nullptr }; + } + + static_assert(sizeof(Option) > sizeof(void*), "Option: Bad null_opt size"); static_assert(alignof(Option) == alignof(void*), "Option: Bad null_opt align"); static_assert(sizeof(Option>) == sizeof(int*), "Option>: Bad null_opt size"); @@ -557,4 +596,5 @@ namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { } using optional::Option; + } diff --git a/include/util.hh b/include/util.hh index d259e9e..f043dc9 100644 --- a/include/util.hh +++ b/include/util.hh @@ -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) - { m_init.~T(); } + { m_init.~T(); m_uninit = {}; } + + [[nodiscard]] + constexpr T assume_init_take() noexcept(std::is_nothrow_move_constructible_v) + { + auto v = std::move(m_init); + assume_init_drop(); + return v; + } constexpr maybe_uninit init(T&& value) noexcept(std::is_nothrow_move_constructible_v) { return maybe_uninit{std::move(value), EXPLICIT_INIT_TAG{}}; }