#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 struct Box { typedef boxable_value_type::type type; 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; }; 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; } 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; }; }