diff --git a/TODO b/TODO index ac9145296..c63d8d5ea 100644 --- a/TODO +++ b/TODO @@ -33,4 +33,4 @@ - auto foo(It first, It last = entity_type{null}) * range based registry::remove and some others? * nested groups: AB/ABC/ABCD/... (hints: sort, check functions) -* pointer payload optimization for delegates +* pointer payload optimization for sigh diff --git a/src/entt/signal/delegate.hpp b/src/entt/signal/delegate.hpp index b91c1979d..acb9740ee 100644 --- a/src/entt/signal/delegate.hpp +++ b/src/entt/signal/delegate.hpp @@ -31,6 +31,10 @@ template Ret(*)(Args...); +template>> +auto to_function_pointer(Ret(*)(Type *, Args...), Payload *) -> Ret(*)(Args...); + + template auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...); @@ -120,19 +124,24 @@ class delegate { data = &value_or_instance; fn = [](const void *payload, std::tuple args) -> Ret { - Type *curr = nullptr; - - if constexpr(std::is_const_v) { - curr = static_cast(payload); - } else { - curr = static_cast(const_cast(payload)); - } - + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); // Ret(...) makes void(...) eat the return values to avoid errors return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(args))...)); }; } + template + void connect(Type *value_or_instance, std::index_sequence) ENTT_NOEXCEPT { + static_assert(std::is_invocable_r_v>...>); + data = value_or_instance; + + fn = [](const void *payload, std::tuple args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + // Ret(...) makes void(...) eat the return values to avoid errors + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(args))...)); + }; + } + public: /*! @brief Function type of the delegate. */ using function_type = Ret(Args...); @@ -158,13 +167,13 @@ public: * or a free function with payload. * @tparam Candidate Member or free function to connect to the delegate. * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. + * @param value_or_instance A valid object that fits the purpose. */ template - delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT : delegate{} { - connect(value_or_instance); + connect(std::forward(value_or_instance)); } /** @@ -190,12 +199,12 @@ public: * * @tparam Candidate Member or free function to connect to the delegate. * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. + * @param value_or_instance A valid object that fits the purpose. */ template - void connect(Type &value_or_instance) ENTT_NOEXCEPT { - constexpr auto extent = internal::function_extent_v(), value_or_instance))>; - connect(value_or_instance, std::make_index_sequence{}); + void connect(Type &&value_or_instance) ENTT_NOEXCEPT { + constexpr auto extent = internal::function_extent_v(), std::forward(value_or_instance)))>; + connect(std::forward(value_or_instance), std::make_index_sequence{}); } /** @@ -297,8 +306,8 @@ delegate(connect_arg_t) ENTT_NOEXCEPT * @tparam Type Type of class or type of payload. */ template -delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT --> delegate>; +delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT +-> delegate(value_or_instance)))>>; } diff --git a/test/entt/signal/delegate.cpp b/test/entt/signal/delegate.cpp index f86811caf..14a0684f5 100644 --- a/test/entt/signal/delegate.cpp +++ b/test/entt/signal/delegate.cpp @@ -6,10 +6,14 @@ int delegate_function(const int &i) { return i*i; } -int curried_function(const int &i, int j) { +int curried_by_ref(const int &i, int j) { return i+j; } +int curried_by_ptr(const int *i, int j) { + return (*i)+j; +} + int non_const_reference(int &i) { return i *= i; } @@ -113,16 +117,30 @@ TEST(Delegate, Comparison) { ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); - lhs.connect<&curried_function>(value); + lhs.connect<&curried_by_ref>(value); - ASSERT_EQ(lhs, (entt::delegate{entt::connect_arg<&curried_function>, value})); + ASSERT_EQ(lhs, (entt::delegate{entt::connect_arg<&curried_by_ref>, value})); ASSERT_TRUE(lhs != rhs); ASSERT_FALSE(lhs == rhs); ASSERT_NE(lhs, rhs); - rhs.connect<&curried_function>(value); + rhs.connect<&curried_by_ref>(value); - ASSERT_EQ(rhs, (entt::delegate{entt::connect_arg<&curried_function>, value})); + ASSERT_EQ(rhs, (entt::delegate{entt::connect_arg<&curried_by_ref>, value})); + ASSERT_FALSE(lhs != rhs); + ASSERT_TRUE(lhs == rhs); + ASSERT_EQ(lhs, rhs); + + lhs.connect<&curried_by_ptr>(&value); + + ASSERT_EQ(lhs, (entt::delegate{entt::connect_arg<&curried_by_ptr>, &value})); + ASSERT_TRUE(lhs != rhs); + ASSERT_FALSE(lhs == rhs); + ASSERT_NE(lhs, rhs); + + rhs.connect<&curried_by_ptr>(&value); + + ASSERT_EQ(rhs, (entt::delegate{entt::connect_arg<&curried_by_ptr>, &value})); ASSERT_FALSE(lhs != rhs); ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); @@ -188,8 +206,10 @@ TEST(Delegate, DeductionGuide) { int value = 0; entt::delegate func{entt::connect_arg<&delegate_function>}; - entt::delegate curried_func{entt::connect_arg<&curried_function>, value}; - entt::delegate curried_func_const{entt::connect_arg<&curried_function>, std::as_const(value)}; + entt::delegate curried_func_with_ref{entt::connect_arg<&curried_by_ref>, value}; + entt::delegate curried_func_with_const_ref{entt::connect_arg<&curried_by_ref>, std::as_const(value)}; + entt::delegate curried_func_with_ptr{entt::connect_arg<&curried_by_ptr>, &value}; + entt::delegate curried_func_with_const_ptr{entt::connect_arg<&curried_by_ptr>, &std::as_const(value)}; entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, functor}; entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, functor}; entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, functor}; @@ -201,8 +221,10 @@ TEST(Delegate, DeductionGuide) { entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, std::as_const(functor)}; static_assert(std::is_same_v); - static_assert(std::is_same_v); - static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(std::is_same_v); @@ -214,8 +236,10 @@ TEST(Delegate, DeductionGuide) { static_assert(std::is_same_v); ASSERT_TRUE(func); - ASSERT_TRUE(curried_func); - ASSERT_TRUE(curried_func_const); + ASSERT_TRUE(curried_func_with_ref); + ASSERT_TRUE(curried_func_with_const_ref); + ASSERT_TRUE(curried_func_with_ptr); + ASSERT_TRUE(curried_func_with_const_ptr); ASSERT_TRUE(member_func_f); ASSERT_TRUE(member_func_g); ASSERT_TRUE(member_func_h); @@ -266,10 +290,15 @@ TEST(Delegate, CurriedFunction) { entt::delegate delegate; const auto value = 3; - delegate.connect<&curried_function>(value); + delegate.connect<&curried_by_ref>(value); ASSERT_TRUE(delegate); ASSERT_EQ(delegate(1), 4); + + delegate.connect<&curried_by_ptr>(&value); + + ASSERT_TRUE(delegate); + ASSERT_EQ(delegate(2), 5); } TEST(Delegate, Constructors) { @@ -278,7 +307,8 @@ TEST(Delegate, Constructors) { entt::delegate empty{}; entt::delegate func{entt::connect_arg<&delegate_function>}; - entt::delegate curr{entt::connect_arg<&curried_function>, value}; + entt::delegate ref{entt::connect_arg<&curried_by_ref>, value}; + entt::delegate ptr{entt::connect_arg<&curried_by_ptr>, &value}; entt::delegate member{entt::connect_arg<&delegate_functor::operator()>, functor}; ASSERT_FALSE(empty); @@ -286,8 +316,11 @@ TEST(Delegate, Constructors) { ASSERT_TRUE(func); ASSERT_EQ(9, func(3)); - ASSERT_TRUE(curr); - ASSERT_EQ(5, curr(3)); + ASSERT_TRUE(ref); + ASSERT_EQ(5, ref(3)); + + ASSERT_TRUE(ptr); + ASSERT_EQ(5, ref(3)); ASSERT_TRUE(member); ASSERT_EQ(6, member(3)); @@ -306,8 +339,8 @@ TEST(Delegate, VoidVsNonVoidReturnType) { } TEST(Delegate, TheLessTheBetter) { - delegate_functor functor; entt::delegate delegate; + delegate_functor functor; // int delegate_function(const int &); delegate.connect<&delegate_function>();