#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>; }; namespace { 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 constexpr decltype(auto) force_ok(auto&& mv) requires(std::is_convertible_v && std::is_default_constructible_v) { struct err { [[gnu::noinline, gnu::cold, noreturn]] static constexpr void _throw() { throw Error{}; } }; if( __builtin_expect(!bool(mv), false)) err::_throw(); return std::forward(mv); } } // _impl (anon) template concept binary_convertible = std::is_convertible_v and std::is_convertible_v; template concept polymorphic_castable = std::derived_from, std::decay_t> or std::derived_from, std::decay_t> or std::is_same_v, std::decay_t> or requires(std::decay_t && from) { { static_cast &&>(from) } -> std::convertible_to; }; template requires(polymorphic_castable) constexpr std::unique_ptr static_uniq_cast(std::unique_ptr&& from) noexcept { return std::unique_ptr { static_cast( std::move(from).release() ) }; } template requires(polymorphic_castable) constexpr std::unique_ptr dynamic_uniq_cast(std::unique_ptr&& from) noexcept { return std::unique_ptr { dynamic_cast( std::move(from).release() ) }; } template requires(polymorphic_castable) constexpr std::unique_ptr uniq_cast(std::unique_ptr&& from) noexcept { if constexpr(Check) return dynamic_uniq_cast(std::move(from)); else return static_uniq_cast(std::move(from)); } #if 0 //TODO: Cannot be implemented with this method constexpr bool is_boxed_value(auto const& value) noexcept { return bool(dynamic_cast(std::addressof(value))); } template constexpr bool is_boxed_type() noexcept { using type = std::remove_reference_t; constexpr type const* VALUE = nullptr; return bool(dynamic_cast(VALUE)); } // We need either: Tagged pointers (XOR, bit-mangled, manual heap, or GOT (external allocation) kind), \ or (preferred): layout of Box to be `{ aligned(T, box_type_tag) } unique*` \ box_type_tag should be: One pointer width, starting at offset *T+1 (aligned to T within internal tuple struct), provides a way to access RTTI for `*T`, a pointer to static (non-allocated) type information or dynamic_cast used lambda would be ideal. #endif template struct Box final { 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)))}; } template U, bool Check = false> constexpr Box upcast() && noexcept(!Check) { return Box { force_ok(uniq_cast(std::move(m_ptr))) }; } template requires(std::derived_from) constexpr Box downcast() && noexcept(!Check) { return Box { force_ok(uniq_cast(std::move(m_ptr))) }; } template U, bool Check = !std::derived_from > // Default dynamic check set to false only if we can statically determine that T is derived from U and the conversion is therfore infallible constexpr Box polycast() && noexcept(!Check) { return Box { force_ok(uniq_cast(std::move(m_ptr))) }; } 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()); } #if 0 // TODO: Identifying if a value `this` pointer is boxed struct alignas(type) inner_layout { constexpr inner_layout() noexcept = default; constexpr inner_layout(const inner_layout&) noexcept = default; constexpr ~inner_layout() noexcept = default; type value; box_type_tag identifier{box_tag_of()}; }; #endif 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. #if 0 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(T&& value) noexcept(std::is_nothrow_constructible_v, T>) { return { std::move(value) }; } #endif template constexpr Box box(T&& value) noexcept(std::is_nothrow_constructible_v, decltype(value)>) { /*if constexpr(requires(decltype(value) v) { static_cast(std::move(v)); }) { return Box::template new_dynamic(std::move(value)); } else */return Box { std::move(value) }; } template constexpr Box box(std::unique_ptr&& value) noexcept { return Box { std::move(value) }; } template /*requires(polymorphic_castable) constexpr Box polybox(T&& value) noexcept { return Box{ std::move(value) }.polycast() }*/ requires(std::derived_from) constexpr Box polybox(T&& value) noexcept { return Box{ std::move(value) }.polycast(); // Convertion from derived to base infallible, no need for check. } template /*requires(polymorphic_castable) constexpr Box polybox(T&& value) noexcept { return Box{ std::move(value) }.polycast() }*/ requires(std::derived_from) constexpr Box polybox(std::unique_ptr&& value) noexcept { return Box{ std::move(value) }.polycast(); // Convertion from derived to base infallible, no need for check. } } 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; }; }