From a7e4a3ffccdb3a86a3c8f58d29b56e97242746ac Mon Sep 17 00:00:00 2001 From: Avril Date: Fri, 5 May 2023 04:34:17 +0100 Subject: [PATCH] Merge dynamic boxing improvements from error-handling in preperation for `boxed::is_boxed_value(T*) noexcept -> bool` implementation considerations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for libexopt's current commit: Great curse − 大凶 --- include/boxed.h | 155 +++++++++++++++++++++++++++++++++++++++++++++++- include/util.hh | 27 ++++++++- 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/include/boxed.h b/include/boxed.h index 4ffdfb1..fd8fae7 100644 --- a/include/boxed.h +++ b/include/boxed.h @@ -10,12 +10,15 @@ #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; } @@ -35,10 +38,96 @@ namespace exopt::types { namespace boxed { 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 { + 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()} {} @@ -90,9 +179,35 @@ namespace exopt::types { namespace boxed { _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())); } @@ -106,6 +221,44 @@ namespace exopt::types { namespace boxed { 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; } diff --git a/include/util.hh b/include/util.hh index f043dc9..ead3315 100644 --- a/include/util.hh +++ b/include/util.hh @@ -12,6 +12,7 @@ #define _EO_CONSTANT_VALUE(X) ([]() { \ struct { \ typedef decltype(X) comptime_constant_t; \ + constexpr static inline auto VALUE = (X); \ consteval operator comptime_constant_t() const noexcept { return (X); } \ } inner; \ return inner; \ @@ -28,7 +29,7 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { constexpr auto comptime_value(auto value) noexcept { using U = decltype(value); - struct inner { + struct inner final { typedef U comptime_constant_t; consteval operator comptime_constant_t() const noexcept { return std::move(value); } U value; @@ -38,7 +39,31 @@ namespace exopt { namespace util [[gnu::visibility("internal")]] { }; return inner{ std::move(value) }; } + + template requires(comptime) + consteval T comptime_eval() noexcept { + if constexpr(requires { + { decltype(value)::VALUE } -> std::convertible_to; + }) { + return decltype(value)::VALUE; + } else return value; + + } + + template requires(comptime) + constexpr inline T comptime_eval_v = comptime_eval(); + + template + consteval T comptime_eval(comptime auto value) noexcept { + return comptime_eval(); + } + + + static_assert(comptime_eval(comptime_value(5.0)) == 5.0, "Bad comptime_eval() consteval"); + static_assert(comptime_eval(_EO_CONSTANT_VALUE(3.f)) == 3.f, "Bad comptime_eval() macro"); + static_assert(std::is_same_v, "Bad comptime_eval() return type consteval"); + static_assert(std::is_same_v, "Bad comptime_eval() return type macro"); static_assert(comptime_value(std::string_view{"hello"}.size()) == 5, "Bad comptime_value()"); static_assert(_EO_CONSTANT_VALUE(std::string_view{"hello world"}) == std::string_view{"hello world"}, "Bad CONSTANT_VALUE()");