From 4d22d2a17abd033ef21d3b6416c1ab4aa0dab568 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 15 Jan 2024 20:17:50 +0800 Subject: [PATCH] Simplify the semantics of facade per spec (#55) * Improve constraints of basic_facade and facade per spec --- proxy.h | 137 +++++++++++++++++------------------ tests/proxy_traits_tests.cpp | 90 +++++++++++++++++++++++ 2 files changed, 155 insertions(+), 72 deletions(-) diff --git a/proxy.h b/proxy.h index 42e3ef1..e34fb45 100644 --- a/proxy.h +++ b/proxy.h @@ -4,6 +4,7 @@ #ifndef _MSFT_PROXY_ #define _MSFT_PROXY_ +#include #include #include #include @@ -110,24 +111,6 @@ struct contains_traits : applicable_traits {}; template struct contains_traits : contains_traits {}; -template struct flattening_traits_impl; -template -struct flattening_traits_impl, T> { using type = T; }; -template -struct flattening_traits_impl, U> - : flattening_traits_impl, U> {}; -template - requires(!contains_traits::applicable) -struct flattening_traits_impl, std::tuple> - : flattening_traits_impl, std::tuple> {}; -template struct flattening_traits { using type = std::tuple; }; -template <> -struct flattening_traits> { using type = std::tuple<>; }; -template -struct flattening_traits> : flattening_traits_impl< - typename flattening_traits::type, - typename flattening_traits>::type> {}; - template struct default_traits { using type = void; }; template struct default_traits { using type = T; }; @@ -138,7 +121,7 @@ struct overload_traits : applicable_traits { template struct resolver { T operator()(Args...); }; template - static constexpr bool applicable_pointer = requires(const P& p, Args... args) + static constexpr bool applicable_ptr = requires(const P& p, Args... args) { D{}(*deduce_address(p), std::forward(args)...); }; template static R dispatcher(const char* erased, Args... args) { @@ -180,17 +163,16 @@ struct dispatch_traits_impl> : applicable_traits, std::tuple::dispatcher_type...>; template - static constexpr bool applicable_pointer = - (overload_traits::template applicable_pointer && ...); + static constexpr bool applicable_ptr = + (overload_traits::template applicable_ptr && ...); template static constexpr dispatcher_types dispatchers{ overload_traits::template dispatcher...}; }; template struct dispatch_traits : inapplicable_traits {}; -template requires(requires { - typename D::overload_types; - D{}; - }) +template + requires(requires { typename D::overload_types; } && + std::is_trivially_default_constructible_v) struct dispatch_traits : dispatch_traits_impl {}; @@ -253,9 +235,10 @@ struct facade_meta_traits : facade_meta_traits_impl< M, typename facade_meta_traits::type> {}; template <> struct facade_meta_traits<> { using type = facade_meta<>; }; -template struct basic_facade_traits_impl; +template +struct basic_facade_traits_impl : inapplicable_traits {}; template -struct basic_facade_traits_impl> { +struct basic_facade_traits_impl> : applicable_traits { using meta_type = typename facade_meta_traits< conditional_meta_tag, conditional_meta_tag, @@ -268,9 +251,22 @@ struct basic_facade_traits_impl> { template static constexpr bool has_dispatch = contains_traits::applicable; }; +template struct basic_facade_traits : inapplicable_traits {}; template -struct basic_facade_traits : basic_facade_traits_impl< - F, typename flattening_traits::type> {}; + requires( + requires { + typename F::dispatch_types; + F::constraints; + typename F::reflection_type; + } && + std::is_same_v && + std::popcount(F::constraints.max_align) == 1u && + F::constraints.max_size % F::constraints.max_align == 0u && + (std::is_void_v || + std::is_trivially_copyable_v)) +struct basic_facade_traits + : basic_facade_traits_impl {}; template struct facade_traits_impl : inapplicable_traits {}; @@ -280,19 +276,19 @@ struct facade_traits_impl> : applicable_traits { typename basic_facade_traits::meta_type, dispatch_meta...>; template - static constexpr bool applicable_pointer = + static constexpr bool applicable_ptr = sizeof(P) <= F::constraints.max_size && alignof(P) <= F::constraints.max_align && has_copyability

(F::constraints.copyability) && has_relocatability

(F::constraints.relocatability) && has_destructibility

(F::constraints.destructibility) && - (dispatch_traits::template applicable_pointer

&& ...) && + (dispatch_traits::template applicable_ptr

&& ...) && (std::is_void_v || std::is_constructible_v< typename F::reflection_type, std::in_place_type_t

>); template static constexpr meta_type meta{std::in_place_type

}; }; -template struct facade_traits : facade_traits_impl< - F, typename flattening_traits::type> {}; +template +struct facade_traits : facade_traits_impl {}; template struct dependent_traits { using type = T; }; template @@ -301,18 +297,14 @@ using dependent_t = typename dependent_traits::type; } // namespace details template -concept basic_facade = requires { - typename F::dispatch_types; - typename std::integral_constant; - typename F::reflection_type; -}; +concept basic_facade = details::basic_facade_traits::applicable; template concept facade = basic_facade && details::facade_traits::applicable; template concept proxiable = facade && details::is_address_deducible && - details::facade_traits::template applicable_pointer

; + details::facade_traits::template applicable_ptr

; template class proxy { @@ -516,8 +508,8 @@ class proxy { decltype(auto) operator()(Args&&... args) const requires(facade> && !std::is_void_v && - details::dependent_t - , Args...>::template has_overload) + details::dependent_t, + Args...>::template has_overload) { return invoke(std::forward(args)...); } private: @@ -592,45 +584,48 @@ proxy make_proxy(T&& value) { // facade types prior to C++26 namespace details { -template -struct one_overload_matching_traits : inapplicable_traits {}; -template - requires(std::is_same_v>) -struct one_overload_matching_traits : applicable_traits {}; +template struct overload_args_traits; +template +struct overload_args_traits { using type = std::tuple; }; template struct overloads_matching_traits : inapplicable_traits {}; -template requires( - one_overload_matching_traits, Os>::applicable || ...) +template struct overloads_matching_traits, std::tuple> - : applicable_traits {}; + : contains_traits, + typename overload_args_traits::type...> {}; template concept matches_overloads = overloads_matching_traits::applicable; -template struct dispatch_tuple_traits { using type = std::tuple; }; -template -struct dispatch_tuple_traits> - { using type = std::tuple; }; - -template -struct tuple_concat_traits { using type = Ts; }; -template -struct tuple_concat_traits, std::tuple, Tss...> - : tuple_concat_traits, Tss...> {}; +template struct flattening_traits_impl; +template +struct flattening_traits_impl, T> { using type = T; }; +template +struct flattening_traits_impl, U> + : flattening_traits_impl, U> {}; +template + requires(!contains_traits::applicable) +struct flattening_traits_impl, std::tuple> + : flattening_traits_impl, std::tuple> {}; +template struct flattening_traits { using type = std::tuple; }; +template <> +struct flattening_traits> { using type = std::tuple<>; }; +template +struct flattening_traits> : flattening_traits_impl< + typename flattening_traits::type, + typename flattening_traits>::type> {}; template requires(sizeof...(Os) > 0u) struct dispatch_prototype { using overload_types = std::tuple; }; - template requires(sizeof...(Ds) > 0u) -struct combined_dispatch : Ds... { - using overload_types = - typename tuple_concat_traits::type; +struct combined_dispatch_prototype : Ds... { + using overload_types = typename flattening_traits< + std::tuple>::type; using Ds::operator()...; }; - template , proxiable_ptr_constraints C = relocatable_ptr_constraints, class R = void> struct facade_prototype { - using dispatch_types = typename dispatch_tuple_traits::type; + using dispatch_types = typename flattening_traits::type; static constexpr proxiable_ptr_constraints constraints = C; using reflection_type = R; }; @@ -643,9 +638,8 @@ struct facade_prototype { struct NAME : ::pro::details::dispatch_prototype<__VA_ARGS__> { \ template \ decltype(auto) operator()(__T& __self, __Args&&... __args) \ - requires( \ - ::pro::details::matches_overloads, \ - std::tuple<__VA_ARGS__>> && \ + requires(::pro::details::matches_overloads, \ + std::tuple<__VA_ARGS__>> && \ requires{ __self.NAME(std::forward<__Args>(__args)...); }) { \ return __self.NAME(std::forward<__Args>(__args)...); \ } \ @@ -654,15 +648,14 @@ struct facade_prototype { struct NAME : ::pro::details::dispatch_prototype<__VA_ARGS__> { \ template \ decltype(auto) operator()(__T& __self, __Args&&... __args) \ - requires( \ - ::pro::details::matches_overloads, \ - std::tuple<__VA_ARGS__>> && \ + requires(::pro::details::matches_overloads, \ + std::tuple<__VA_ARGS__>> && \ requires{ FUNC(__self, std::forward<__Args>(__args)...); }) { \ return FUNC(__self, std::forward<__Args>(__args)...); \ } \ } #define PRO_DEF_COMBINED_DISPATCH(NAME, ...) \ - struct NAME : ::pro::details::combined_dispatch<__VA_ARGS__> {} + struct NAME : ::pro::details::combined_dispatch_prototype<__VA_ARGS__> {} #define PRO_MAKE_DISPATCH_PACK(...) std::tuple<__VA_ARGS__> #define PRO_DEF_FACADE(NAME, ...) \ struct NAME : ::pro::details::facade_prototype<__VA_ARGS__> {} diff --git a/tests/proxy_traits_tests.cpp b/tests/proxy_traits_tests.cpp index 720181a..b426682 100644 --- a/tests/proxy_traits_tests.cpp +++ b/tests/proxy_traits_tests.cpp @@ -143,4 +143,94 @@ static_assert(std::is_nothrow_assignable_v, MockTrivia static_assert(std::is_nothrow_constructible_v, MockFunctionPtr>); static_assert(std::is_nothrow_assignable_v, MockFunctionPtr>); +struct ReflectionOfSmallPtr { + template requires(sizeof(P) <= sizeof(void*)) + constexpr ReflectionOfSmallPtr(std::in_place_type_t

) {} +}; +PRO_DEF_FACADE(RelocatableFacadeWithReflection, PRO_MAKE_DISPATCH_PACK(), pro::relocatable_ptr_constraints, ReflectionOfSmallPtr); +static_assert(!pro::proxiable); +static_assert(!pro::proxiable); +static_assert(pro::proxiable); +static_assert(pro::proxiable); +static_assert(pro::proxiable); + +struct BadFacade_MissingDispatchTypes { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-const-variable" +#endif // __clang__ + static constexpr auto constraints = pro::relocatable_ptr_constraints; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_BadDispatchTypes { + using dispatch_types = int; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-const-variable" +#endif // __clang__ + static constexpr auto constraints = pro::relocatable_ptr_constraints; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_MissingConstraints { + using dispatch_types = std::tuple<>; + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_BadConstraints_UnexpectedType { + using dispatch_types = std::tuple<>; + static constexpr auto constraints = 0; + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_BadConstraints_BadAlignment { + using dispatch_types = std::tuple<>; + static constexpr pro::proxiable_ptr_constraints constraints{ + .max_size = 6u, + .max_align = 6u, // Should be a power of 2 + .copyability = pro::constraint_level::none, + .relocatability = pro::constraint_level::nothrow, + .destructibility = pro::constraint_level::nothrow, + }; + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_BadConstraints_BadSize { + using dispatch_types = std::tuple<>; + static constexpr pro::proxiable_ptr_constraints constraints{ + .max_size = 6u, // Should be a multiple of max_alignment + .max_align = 4u, + .copyability = pro::constraint_level::none, + .relocatability = pro::constraint_level::nothrow, + .destructibility = pro::constraint_level::nothrow, + }; + using reflection_type = void; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_MissingReflectionType { + using dispatch_types = std::tuple<>; + static constexpr auto constraints = pro::relocatable_ptr_constraints; +}; +static_assert(!pro::basic_facade); + +struct BadFacade_BadReflectionType { + using dispatch_types = std::tuple<>; + static constexpr auto constraints = pro::relocatable_ptr_constraints; + using reflection_type = std::unique_ptr; +}; +static_assert(!pro::basic_facade); + } // namespace