#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::NullDerefException{}; #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 T> constexpr auto uniq_erase_type(std::unique_ptr&& from) noexcept { struct deleter final /*: public ErasedTypeDeleter XXX <- Is this useful, for unerasing the type, maybe?*/ { typedef T type; /*[[gnu::cold, noreturn, gnu::noinline]] constexpr static void _throw() { throw ptr::NullException{}; } public:*/ constexpr void opreator()(void *p) noexcept(std::is_nothrow_destructible_v) { /* T* ptr; if constexpr(DOES_CHECK) { if(__builtin_expect(!(ptr = dynamic_cast(p)), false)) _throw(); } else ptr = static_cast(p); delete ptr;*/ delete static_cast(p); } constexpr T* back_cast(void *p) noexcept { return static_cast(p); } constexpr deleter() noexcept = default; constexpr deleter(deleter const&) noexcept = default; constexpr deleter(deleter &&) noexcept = default; constexpr deleter& operator=(deleter const&) noexcept = default; constexpr deleter& operator=(deleter &&) noexcept = default; constexpr ~deleter() noexcept = default; }; return std::unique_ptr { // Cannot retain type information of most-derived class from this: \ dynamic_cast static_cast (std::move(from).release()) }; } template T, typename D> requires(requires{ typename std::unique_ptr; requires(!std::is_same_v::deleter_type, std::unique_ptr::deleter_type); }) constexpr auto uniq_erase_type(std::unique_ptr&& from) noexcept { class deleter final { D m_del; public: constexpr void operator()(void* p) noexcept(std::is_nothrow_invocable_v) { m_del(static_cast(p)); } constexpr T* back_cast(void* p) noexcept { return static_cast(p); } constexpr deleter(D&& deleter) noexcept(std::is_nothrow_move_constructible_v) : m_del(std::move(deleter)) {} constexpr deleter() noexcept(std::is_nothrow_default_constructible_v) requires(std::is_default_constructible_v) =default;//: m_del() {} constexpr deleter(const deleter&) noexcept(std::is_nothrow_copy_constructible_v) = default; constexpr deleter(deleter&&) noexcept(std::is_nothrow_move_constructible_v) = default; constexpr deleter& operator=(const deleter&) noexcept(std::is_nothrow_copy_assignable_v) = default; constexpr deleter& operator=(deleter&&) noexcept(std::is_nothrow_move_assignable_v) = default; constexpr ~deleter() noexcept(std::is_nothrow_destructible_v) requires(std::is_trivially_destructible_v) = default; constexpr ~deleter() noexcept(std::is_nothrow_destructible_v) {} }; if constexpr(std::is_default_constructible_v) return std::unique_ptr { static_cast (std::move(from).release()), deleter() }; else { deleter&& del{std::move(from.get_deleter())}; return std::unique_ptr { static_cast (std::move(from).release()), deleter{std::move(del)} }; } } 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)); } template struct alignas(std::unique_ptr) 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)))}; } 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(!_EO(DEBUG)) { #if DEBUG ptr::throw_if_null(ptr); #endif return Box{ std::unique_ptr{ ptr } }; } constexpr static Box from_raw_ptr(std::unique_ptr&& uniq) { if(__builtin_expect(!bool(uniq), false)) ptr::throw_null_exception(); _EO_ASSUME(uniq.get() != nullptr); return Box { std::move(uniq) }; } constexpr static Box from_raw_ptr_unchecked(std::unique_ptr&& uniq) noexcept(!_EO(DEBUG)) { #if DEBUG if(__builtin_expect(!uniq, false)) ptr::throw_null_exception(); #else _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; protected: // For types that may need to invalidate the Box guarantee without exposing it in API, they can inherit (non-virtual). // For unsafe operations, wether or not to apply checks. enum unsafe_t { UNSAFE, }; enum maybe_unsafe_t { CHECKED, DEBUG_CHECKED, }; constexpr Box(util::exact_type auto u, std::unique_ptr&& ptr) noexcept : m_ptr(std::move(ptr)) { (void)u; } constexpr Box(util::exact_type auto u, Box const& copy) noexcept : m_ptr(uniq_clone(copy.as_unique(u))) {} constexpr static Box from_raw_ptr(unsafe_t u, std::unique_ptr&& uniq) noexcept { return Box{u, std::move(uniq)}; } constexpr std::unique_ptr& as_unique(unsafe_t) & noexcept { return m_ptr; } constexpr std::unique_ptr const & as_unique(unsafe_t) const& noexcept { return m_ptr; } constexpr std::unique_ptr&& as_unique(unsafe_t) && noexcept { return std::move(m_ptr); } constexpr std::unique_ptr const&& as_unique(unsafe_t) const&& noexcept { return std::move(m_ptr); } constexpr T* as_unsafe_ptr(unsafe_t) const noexcept { return m_ptr.get(); } constexpr std::unique_ptr swap_unsafe_ptr(unsafe_t u, T* raw) noexcept { return (void)u; std::exchange(std::move(m_ptr), std::unique_ptr{ raw }); } constexpr void set_unsafe_ptr(unsafe_t 8, T* raw) noexcept { (void)u; m_ptr.reset(raw); } constexpr decltype(auto) set_unsafe(unsafe_t u, std::unique_ptr&& ptr) noexcept { (void)u; return std::exchange(std::move(m_ptr), std::move(ptr)); } constexpr auto& swap_unsafe(unsafe_t, std::unique_ptr& ptr) noexcept { using std::swap; swap(m_ptr, ptr); return m_ptr; } constexpr T* release_unsafe(unsafe_t) noexcept { return m_ptr.release(); } private: constexpr explicit Box(std::unique_ptr&& ptr) noexcept : m_ptr(std::move(ptr)) { _EO_ASSUME(m_ptr.get()); } std::unique_ptr m_ptr; // Unique m_ptr; }; static_assert(util::shares_layout, _Complex double*>, "Invalid Box memory layout: More than just T*"); static_assert(util::shares_layout, std::unique_ptr<_Complex double>>, "Invalid Box memory layout: More than just std::unique_ptr"); template concept is_boxable = is_boxable_v; template constexpr inline bool is_nothrow_boxable_v = is_boxable_v && std::is_nothrow_constructible_v, T>; 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; } } template requires(requires(T const& o) { static_cast(o); }) constexpr Box dynamic_box_cast(Box const& b) { return Box::from_raw_ptr(std::addressof(dynamic_cast(*b))); } #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; } //TODO: See phone notes on correctly implementing the nullopt for Box #if 0 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; }; } #endif