#pragma once #include #include #include "pointer.h" #include "util.hh" #include "exopt.h" namespace exopt::types { namespace boxed { template struct [[gnu::visibility("internal")]] boxable_value_type { using type = std::conditional_t ,std::reference_wrapper > ,T>; }; template constexpr std::unique_ptr uniq_clone(std::unique_ptr const& c) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v) { if(__builtin_expect(bool(c), true)) return std::make_unique(*c); return nullptr; } template constexpr std::unique_ptr uniq_clone_unsafe(std::unique_ptr const& c) #ifndef DEBUG noexcept(std::is_nothrow_copy_constructible_v) #endif requires(std::is_copy_constructible_v) { if(__builtin_expect(!bool(c), false)) #ifdef DEBUG throw ptr::NullException{}; #else __builtin_unreachable(); #endif return std::make_unique(*c); } template concept binary_convertible = std::is_convertible_v and std::is_convertible_v; template 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()} {} template requires(std::is_constructible_v) constexpr Box(Args&&... ctor) noexcept(std::is_nothrow_constructible_v) : m_ptr{std::make_unique(std::forward(ctor)...)} {} constexpr Box(T&& value) noexcept(std::is_nothrow_constructible_v) : m_ptr{std::make_unique(std::move(value))} {} constexpr Box(Box&&) noexcept = default; constexpr Box& operator=(Box&&) noexcept = default; constexpr Box(const Box& copy) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v) : m_ptr{uniq_clone_unsafe(copy.m_ptr)} {} constexpr Box& operator=(Box const& copy) noexcept(std::is_nothrow_copy_assignable_v) requires(std::is_copy_assignable_v) { if(__builtin_expect(this != std::addressof(copy), true)) { m_ptr = uniq_clone_unsafe(copy.m_ptr); } 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; }; #define _EO_ADD_RV std::add_rvalue_reference_v template constexpr inline bool is_boxable_v = binary_convertible<_EO_ADD_RV, _EO_ADD_RV > and requires(T&& value) { typename Box; typename Box::type; { std::make_unique(std::move(value)) } -> std::same_as>; }; template concept is_boxable = is_boxable_v; template constexpr inline bool is_nothrow_boxable_v = is_boxable_v && std::is_nothrow_constructible_v, T>; #undef _EO_ADD_RV 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. template requires(std::derived_from) constexpr Box box(R&& value, util::comptime auto Check = _EO_CONSTANT_VALUE(false)) noexcept(std::is_nothrow_invocable_v< Box::new_dynamic) //XXX: This seems illegal... { return Box::template new_dynamic(std::move(value)); } template constexpr Box box(std::convertible_to auto&& value) noexcept(std::is_nothrow_constructible_v, std::add_rvalue_reference_v>) { /*if constexpr(requires(decltype(value) v) { static_cast(std::move(v)); }) { return Box::template new_dynamic(std::move(value)); } else */return { static_cast(std::move(value)) }; } template constexpr Box box(T&& value) noexcept(std::is_nothrow_constructible_v, T>) { return { std::move(value) }; } } using boxed::Box; } namespace exopt::types { namespace optional [[gnu::visibility("internal")]] { template struct null_optimise > { constexpr static inline bool value = true; //XXX: Eh.. Idk if Option's lifetime design can make this work... using held_type = boxed::Box; using type = boxed::Box&; constexpr static decltype(auto) convert_to_held(std::add_rvalue_reference_t t) noexcept { return std::move(t); } constexpr static decltype(auto) convert_from_held(std::add_rvalue_reference_t t) noexcept { return std::forward(t); } constexpr static inline T* sentinel = nullptr; }; }