From 4013b00840322339352db0ce260733b37f569822 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Tue, 2 Apr 2024 13:15:06 +0800 Subject: [PATCH] Support default implementation of a dispatch (#79) --- proxy.h | 292 +++++++++++++++---------------- tests/proxy_invocation_tests.cpp | 55 ++++-- 2 files changed, 176 insertions(+), 171 deletions(-) diff --git a/proxy.h b/proxy.h index 3b0a063..c41eee4 100644 --- a/proxy.h +++ b/proxy.h @@ -111,9 +111,6 @@ consteval bool has_destructibility(constraint_level level) { default: return false; } } -consteval bool requires_lifetime_meta(constraint_level level) { - return level > constraint_level::none && level < constraint_level::trivial; -} // As per std::to_address() wording in [pointer.conversion] template struct ptr_traits : inapplicable_traits {}; @@ -121,69 +118,90 @@ template requires(requires(const P p) { std::pointer_traits

::to_address(p); } || requires(const P p) { p.operator->(); }) struct ptr_traits

: applicable_traits { - static auto to_address(const P& p) noexcept { return std::to_address(p); } - using reference_type = typename ptr_traits< - decltype(to_address(std::declval()))>::reference_type; + static auto& dereference(const P& p) noexcept { return *std::to_address(p); } + using target_type = std::remove_pointer_t< + decltype(std::to_address(std::declval()))>; }; template struct ptr_traits : applicable_traits { - static auto to_address(T* p) noexcept { return p; } - using reference_type = T&; + static T& dereference(T* p) noexcept { return *p; } + using target_type = T; }; -template struct overload_traits : inapplicable_traits {}; -template -struct overload_traits : applicable_traits { - using dispatcher_type = R (*)(const char*, Args...); - struct resolver { R (*operator()(Args...))(Args...); }; - using forwarding_argument_types = std::tuple; - template - struct meta_provider { - template - static R dispatcher(const char* erased, Args... args) { - auto ptr = ptr_traits

::to_address(*reinterpret_cast(erased)); - if constexpr (std::is_void_v) { - D{}(*ptr, std::forward(args)...); +template +consteval bool is_invoker_well_formed() { + if constexpr (std::is_trivially_default_constructible_v) { + if constexpr (std::is_void_v) { + if constexpr (NE) { + return std::is_nothrow_invocable_r_v; } else { - return D{}(*ptr, std::forward(args)...); + return std::is_invocable_r_v; + } + } else { + if constexpr (NE) { + return std::is_nothrow_invocable_r_v; + } else { + return std::is_invocable_r_v; } } - }; + } + return false; +} +template +concept invocable_dispatch = requires { typename D::template invoker; } && + is_invoker_well_formed< + typename D::template invoker, T, NE, R, Args...>(); - template - static constexpr bool applicable_callable = - std::is_invocable_r_v; - template - static constexpr bool applicable_ptr = - applicable_callable::reference_type>; - static constexpr bool is_noexcept = false; -}; -template -struct overload_traits : applicable_traits { - using dispatcher_type = R (*)(const char*, Args...) noexcept; - struct resolver { R (*operator()(Args...))(Args...) noexcept; }; - using forwarding_argument_types = std::tuple; +template +using func_ptr_t = std::conditional_t< + NE, R (*)(Args...) noexcept, R (*)(Args...)>; + +template +R invoke_dispatch(Args&&... args) { + if constexpr (std::is_void_v) { + F{}(std::forward(args)...); + } else { + return F{}(std::forward(args)...); + } +} +template +R dispatcher_default_impl(const char*, Args... args) noexcept(NE) { + return invoke_dispatch(std::forward(args)...); +} +template +R dispatcher_impl(const char* erased, Args... args) noexcept(NE) { + return invoke_dispatch(ptr_traits

::dereference( + *reinterpret_cast(erased)), std::forward(args)...); +} +template +struct overload_traits_impl : applicable_traits { template struct meta_provider { template - static R dispatcher(const char* erased, Args... args) noexcept { - auto ptr = ptr_traits

::to_address(*reinterpret_cast(erased)); - if constexpr (std::is_void_v) { - D{}(*ptr, std::forward(args)...); + static constexpr func_ptr_t get() { + if constexpr (invocable_dispatch< + D, typename ptr_traits

::target_type, NE, R, Args...>) { + return &dispatcher_impl::target_type>, NE, R, Args...>; } else { - return D{}(*ptr, std::forward(args)...); + return &dispatcher_default_impl< + typename D::template invoker, NE, R, Args...>; } } }; - - template - static constexpr bool applicable_callable = - std::is_nothrow_invocable_r_v; + struct resolver { func_ptr_t operator()(Args...); }; template - static constexpr bool applicable_ptr = - applicable_callable::reference_type>; - static constexpr bool is_noexcept = true; + static constexpr bool applicable_ptr = invocable_dispatch< + D, typename ptr_traits

::target_type, NE, R, Args...> || + invocable_dispatch; + static constexpr bool is_noexcept = NE; }; +template struct overload_traits : inapplicable_traits {}; +template +struct overload_traits : overload_traits_impl {}; +template +struct overload_traits + : overload_traits_impl {}; template struct nullable_traits : inapplicable_traits {}; template @@ -200,11 +218,11 @@ struct dispatcher_meta { constexpr dispatcher_meta() noexcept : dispatcher(nullptr) {} template constexpr explicit dispatcher_meta(std::in_place_type_t

) noexcept - : dispatcher(&MP::template dispatcher

) {} + : dispatcher(MP::template get

()) {} bool has_value() const noexcept { return dispatcher != nullptr; } void reset() noexcept { dispatcher = nullptr; } - decltype(&MP::template dispatcher) dispatcher; + decltype(MP::template get()) dispatcher; }; template @@ -246,63 +264,49 @@ struct dispatch_traits_impl : applicable_traits { template struct dispatch_traits : inapplicable_traits {}; template requires(requires { typename D::overload_types; } && - is_tuple_like_well_formed() && - std::is_trivially_default_constructible_v) + is_tuple_like_well_formed()) struct dispatch_traits : instantiated_t< dispatch_traits_impl, typename D::overload_types, D> {}; -template struct copyability_meta_provider; -template <> -struct copyability_meta_provider { +template +struct copyability_meta_provider { template - static void dispatcher(char* self, const char* rhs) { - std::construct_at(reinterpret_cast(self), - *reinterpret_cast(rhs)); + static constexpr func_ptr_t get() { + return [](char* self, const char* rhs) noexcept(NE) { + std::construct_at(reinterpret_cast(self), + *reinterpret_cast(rhs)); + }; } }; -template <> -struct copyability_meta_provider { +template +struct relocatability_meta_provider { template - static void dispatcher(char* self, const char* rhs) noexcept { - std::construct_at(reinterpret_cast(self), - *reinterpret_cast(rhs)); + static constexpr func_ptr_t get() { + return [](char* self, char* rhs) noexcept(NE) { + std::construct_at(reinterpret_cast(self), + std::move(*reinterpret_cast(rhs))); + std::destroy_at(reinterpret_cast(rhs)); + }; } }; -template struct relocatability_meta_provider; -template <> -struct relocatability_meta_provider { +template +struct destructibility_meta_provider { template - static void dispatcher(char* self, char* rhs) { - std::construct_at(reinterpret_cast(self), - std::move(*reinterpret_cast(rhs))); - std::destroy_at(reinterpret_cast(rhs)); + static constexpr func_ptr_t get() { + return [](char* self) noexcept(NE) + { std::destroy_at(reinterpret_cast(self)); }; } }; -template <> -struct relocatability_meta_provider { - template - static void dispatcher(char* self, char* rhs) noexcept { - std::construct_at(reinterpret_cast(self), - std::move(*reinterpret_cast(rhs))); - std::destroy_at(reinterpret_cast(rhs)); - } -}; -template struct destructibility_meta_provider; -template <> -struct destructibility_meta_provider { - template - static void dispatcher(char* self) - { std::destroy_at(reinterpret_cast(self)); } -}; -template <> -struct destructibility_meta_provider { - template - static void dispatcher(char* self) noexcept - { std::destroy_at(reinterpret_cast(self)); } -}; -template