diff --git a/proxy.h b/proxy.h index 6420f7d..7c0fee2 100644 --- a/proxy.h +++ b/proxy.h @@ -204,10 +204,11 @@ inline void destruction_default_dispatcher(std::byte*) noexcept {} template struct overload_traits_impl : applicable_traits { + using dispatcher_type = func_ptr_t; template struct meta_provider { template - static constexpr func_ptr_t get() { + static constexpr dispatcher_type get() { if constexpr (invocable_dispatch< D, NE, R, typename ptr_traits

::target_type&, Args...>) { return &invocation_dispatcher_ref; @@ -223,6 +224,8 @@ struct overload_traits_impl : applicable_traits { static constexpr bool applicable_ptr = invocable_dispatch_ptr; static constexpr bool is_noexcept = NE; + template + static constexpr bool matches = std::is_invocable_v; }; template struct overload_traits : inapplicable_traits {}; template @@ -492,11 +495,8 @@ struct meta_ptr : M { template struct proxy_helper { - template - static const M& get_meta(const proxy& p) noexcept - { return *static_cast(p.meta_.operator->()); } - static inline const std::byte* get_ptr(const proxy& p) noexcept - { return p.ptr_; } + static inline const auto& get_meta(const proxy& p) noexcept + { return *p.meta_.operator->(); } }; template using proxy_overload = typename facade_traits< @@ -516,6 +516,7 @@ class proxy : public details::facade_traits::base { friend struct details::proxy_helper; using Traits = details::facade_traits; static_assert(Traits::applicable); + using Meta = typename Traits::meta; template static constexpr bool HasNothrowPolyConstructor = std::conditional_t< @@ -558,6 +559,46 @@ class proxy : public details::facade_traits::base { static constexpr bool HasMoveAssignment = HasMoveConstructor && HasDestructor; public: + template + class dispatch_ptr { + using OverloadTraits = details::overload_traits; + using DispatcherType = typename OverloadTraits::dispatcher_type; + + public: + constexpr dispatch_ptr() noexcept = default; + constexpr dispatch_ptr(const dispatch_ptr&) noexcept = default; + dispatch_ptr& operator=(const dispatch_ptr&) noexcept = default; + +#if defined(_MSC_VER) && !defined(__clang__) + template + constexpr explicit dispatch_ptr(std::in_place_type_t) noexcept + : offset_(offsetof(Meta, template dispatcher_meta>::dispatcher)) {} + DispatcherType get_dispatcher(const Meta& meta) const noexcept { + return *reinterpret_cast( + reinterpret_cast(&meta) + offset_); + } + + private: + std::size_t offset_; +#else + template + constexpr explicit dispatch_ptr(std::in_place_type_t) noexcept + : ptr_(&details::dispatcher_meta>::dispatcher) {} + DispatcherType get_dispatcher(const Meta& meta) const noexcept + { return meta.*ptr_; } + + private: + DispatcherType Meta::* ptr_; +#endif // defined(_MSC_VER) && !defined(__clang__) + }; + template + static auto consteval get_dispatch_ptr() + requires(requires { typename details::proxy_overload; }) { + return dispatch_ptr>{ + std::in_place_type}; + } proxy() noexcept = default; proxy(std::nullptr_t) noexcept : proxy() {} proxy(const proxy& rhs) noexcept(HasNothrowCopyConstructor) @@ -694,15 +735,25 @@ class proxy : public details::facade_traits::base { return initialize

(il, std::forward(args)...); } + template + friend auto operator->*(const proxy& p, dispatch_ptr ptd) noexcept { + return [&p, ptd](Args&&... args) + noexcept(details::overload_traits::is_noexcept) -> decltype(auto) + requires(details::overload_traits::template matches) { + return ptd.get_dispatcher(*p.meta_.operator->())( + p.ptr_, std::forward(args)...); + }; + } + private: template P& initialize(Args&&... args) { std::construct_at(reinterpret_cast(ptr_), std::forward(args)...); - meta_ = details::meta_ptr{std::in_place_type

}; + meta_ = details::meta_ptr{std::in_place_type

}; return *std::launder(reinterpret_cast(ptr_)); } - details::meta_ptr meta_; + details::meta_ptr meta_; alignas(F::constraints.max_align) std::byte ptr_[F::constraints.max_size]; }; @@ -711,18 +762,14 @@ decltype(auto) proxy_invoke(const proxy& p, Args&&... args) noexcept(details::overload_traits> ::is_noexcept) requires(requires { typename details::proxy_overload; }) { - return details::proxy_helper - ::template get_meta>::template meta_provider>>(p) - .dispatcher(details::proxy_helper::get_ptr(p), - std::forward(args)...); + constexpr auto ptd = proxy::template get_dispatch_ptr(); + return (p->*ptd)(std::forward(args)...); } template const R& proxy_reflect(const proxy& p) noexcept requires(details::facade_traits::template has_refl) - { return details::proxy_helper::template get_meta(p); } + { return details::proxy_helper::get_meta(p); } namespace details { @@ -1282,6 +1329,7 @@ ___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(^=) ___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(<<=) ___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(>>=) ___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL(,) +___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL(->*) #undef ___PRO_OPERATOR_DISPATCH_TRAITS_EXTENDED_BINARY_IMPL #undef ___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index 9af4a8a..5dc0971 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -2,14 +2,15 @@ // Licensed under the MIT License. #include -#ifdef _MSC_VER +#include +#if defined(_MSC_VER) && !defined(__clang__) #pragma warning(push) #pragma warning(disable: 4834) // False alarm from MSVC: warning C4834: discarding return value of function with [[nodiscard]] attribute -#endif // _MSC_VER +#endif // defined(_MSC_VER) && !defined(__clang__) #include "proxy.h" -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) -#endif // _MSC_VER +#endif // defined(_MSC_VER) && !defined(__clang__) namespace { @@ -46,6 +47,7 @@ PRO_DEF_OPERATOR_DISPATCH(OpBitwiseXorAssignment, "^="); PRO_DEF_OPERATOR_DISPATCH(OpLeftShiftAssignment, "<<="); PRO_DEF_OPERATOR_DISPATCH(OpRightShiftAssignment, ">>="); PRO_DEF_OPERATOR_DISPATCH(OpComma, ","); +PRO_DEF_OPERATOR_DISPATCH(OpPtrToMem, "->*"); PRO_DEF_OPERATOR_DISPATCH(OpArrow, "->"); PRO_DEF_OPERATOR_DISPATCH(OpParentheses, "()"); PRO_DEF_OPERATOR_DISPATCH(OpBrackets, "[]"); @@ -72,6 +74,7 @@ PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpCaret, "^"); PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpLeftShift, "<<"); PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpRightShift, ">>"); PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpComma, ","); +PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpPtrToMem, "->*"); PRO_DEF_CONVERTION_DISPATCH(ConvertToInt, int); @@ -79,12 +82,20 @@ struct CommaTester { public: explicit CommaTester(int v) : value_(v) {} int operator,(int v) { return value_ + v; } - friend int operator,(int v, CommaTester c) { return v * c.value_; } + friend int operator,(int v, CommaTester self) { return v * self.value_; } private: int value_; }; +struct PtrToMemTester { +public: + explicit PtrToMemTester(int v) : value_(v) {} + friend int operator->*(int v, PtrToMemTester self) { return v * self.value_; } + +private: + int value_; +}; } // namespace TEST(ProxyDispatchTests, TestOpPlus) { @@ -340,6 +351,28 @@ TEST(ProxyDispatchTests, TestOpComma) { ASSERT_EQ((p, 6), 9); } +TEST(ProxyDispatchTests, TestOpPtrToMem) { + struct Base1 { int a; int b; int c; }; + struct Base2 { double x; }; + struct Derived1 : Base1 { int x; }; + struct Derived2 : Base2, Base1 { int d; }; + struct TestFacade : pro::facade_builder::add_convention::build {}; + Derived1 v1{}; + Derived2 v2{}; + pro::proxy p1 = &v1, p2 = &v2; + std::vector fields{&Base1::a, &Base1::b, &Base1::c}; + for (int i = 0; i < std::ssize(fields); ++i) { + p1->*fields[i] = i + 1; + p2->*fields[i] = i + 1; + } + ASSERT_EQ(v1.a, 1); + ASSERT_EQ(v1.b, 2); + ASSERT_EQ(v1.c, 3); + ASSERT_EQ(v2.a, 1); + ASSERT_EQ(v2.b, 2); + ASSERT_EQ(v2.c, 3); +} + TEST(ProxyDispatchTests, TestOpArrow) { struct TestFacade : pro::facade_builder::add_convention::build {}; int v = 12; @@ -539,6 +572,13 @@ TEST(ProxyDispatchTests, TestPreOpComma) { ASSERT_EQ((7, p), 21); } +TEST(ProxyDispatchTests, TestPreOpPtrToMem) { + struct TestFacade : pro::facade_builder::add_convention::build {}; + PtrToMemTester v{3}; + pro::proxy p = &v; + ASSERT_EQ(2->*p, 6); +} + TEST(ProxyDispatchTests, TestConvertion) { struct TestFacade : pro::facade_builder::add_convention::build {}; double v = 12.3; diff --git a/tests/proxy_invocation_tests.cpp b/tests/proxy_invocation_tests.cpp index 7920fcf..7e4acf4 100644 --- a/tests/proxy_invocation_tests.cpp +++ b/tests/proxy_invocation_tests.cpp @@ -314,3 +314,13 @@ TEST(ProxyInvocationTests, TestObserverDispatch) { ASSERT_EQ(ToString(p), "123"); } +TEST(ProxyInvocationTests, TestPtrToMember) { + using Runnable = pro::proxy>; + constexpr auto OpCallPtr = Runnable::get_dispatch_ptr(); + static_assert(std::is_same_v>); + int side_effects = 0; + auto f = [&] { side_effects = 1; }; + Runnable p = &f; + (p->*OpCallPtr)(); + ASSERT_EQ(side_effects, 1); +}