diff --git a/TODO b/TODO index c63d8d5ea..5fd6f00c5 100644 --- a/TODO +++ b/TODO @@ -33,4 +33,3 @@ - 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 sigh diff --git a/src/entt/signal/delegate.hpp b/src/entt/signal/delegate.hpp index 3599ed935..4da3f3910 100644 --- a/src/entt/signal/delegate.hpp +++ b/src/entt/signal/delegate.hpp @@ -27,8 +27,8 @@ template auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); -template>> -auto to_function_pointer(Ret(*)(Type &, Args...), const Payload &) -> Ret(*)(Args...); +template>> +auto to_function_pointer(Ret(*)(Type &, Args...), const Payload *) -> Ret(*)(Args...); template>> @@ -36,15 +36,15 @@ auto to_function_pointer(Ret(*)(Type *, Args...), const Payload *) -> Ret(*)(Arg template -auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...); +auto to_function_pointer(Ret(Class:: *)(Args...), const Class *) -> Ret(*)(Args...); template -auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...); +auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class *) -> Ret(*)(Args...); template -auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)(); +auto to_function_pointer(Type Class:: *, const Class *) -> Type(*)(); template @@ -171,13 +171,27 @@ 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 object that fits the purpose. + * @param value_or_instance A valid reference 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(std::forward(value_or_instance)); + connect(value_or_instance); + } + + /** + * @brief Constructs a delegate and connects a member for a given instance + * 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 pointer that fits the purpose. + */ + template + delegate(connect_arg_t, Type *value_or_instance) ENTT_NOEXCEPT + : delegate{} + { + connect(value_or_instance); } /** @@ -203,12 +217,33 @@ 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 object that fits the purpose. + * @param value_or_instance A valid reference that fits the purpose. */ template - void connect(Type &&value_or_instance) ENTT_NOEXCEPT { - constexpr auto extent = internal::function_extent_v>; - connect(std::forward(std::forward(value_or_instance)), std::make_index_sequence{}); + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + constexpr auto extent = internal::function_extent_v>; + connect(value_or_instance, std::make_index_sequence{}); + } + + /** + * @brief Connects a member function for a given instance or a free function + * with payload to a delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @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 pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + constexpr auto extent = internal::function_extent_v>; + connect(value_or_instance, std::make_index_sequence{}); } /** @@ -310,8 +345,23 @@ 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>>; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the function type of the delegate directly from a member + * or a free function with payload provided to the constructor. + * + * @param value_or_instance A valid pointer that fits the purpose. + * @tparam Candidate Member or free function to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type *value_or_instance) ENTT_NOEXCEPT +-> delegate>>; } diff --git a/src/entt/signal/sigh.hpp b/src/entt/signal/sigh.hpp index fcea9ca82..53c959c08 100644 --- a/src/entt/signal/sigh.hpp +++ b/src/entt/signal/sigh.hpp @@ -305,7 +305,7 @@ class sink { using difference_type = typename std::iterator_traits::difference_type; template - static void release(Type &value_or_instance, void *signal) { + static void release(Type value_or_instance, void *signal) { sink{*static_cast(signal)}.disconnect(value_or_instance); } @@ -371,6 +371,27 @@ public: return other; } + /** + * @brief Returns a sink that connects before a given member function or + * free function with payload. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type *value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + /** * @brief Returns a sink that connects before a given instance or specific * payload. @@ -461,7 +482,37 @@ public: signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; - conn.template connect<&release>(value_or_instance); + conn.template connect<&release>(value_or_instance); + return { std::move(conn), signal }; + } + + /** + * @brief Connects a member function or a free function with payload to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Member or free function to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type *value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); return { std::move(conn), signal }; } @@ -492,6 +543,21 @@ public: calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } + /** + * @brief Disconnects a member function or a free function with payload from + * a signal. + * @tparam Candidate Member or free function to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + /** * @brief Disconnects member functions or free functions based on an * instance or specific payload. diff --git a/test/entt/signal/delegate.cpp b/test/entt/signal/delegate.cpp index 14a0684f5..4feed99ae 100644 --- a/test/entt/signal/delegate.cpp +++ b/test/entt/signal/delegate.cpp @@ -212,13 +212,13 @@ TEST(Delegate, DeductionGuide) { 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}; - entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, std::as_const(functor)}; + entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor}; + entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)}; entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor}; entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)}; entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor}; - entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, functor}; - entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, std::as_const(functor)}; + entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor}; + 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); @@ -330,8 +330,8 @@ TEST(Delegate, VoidVsNonVoidReturnType) { delegate_functor functor; entt::delegate func{entt::connect_arg<&delegate_function>}; - entt::delegate member{entt::connect_arg<&delegate_functor::operator()>, functor}; - entt::delegate cmember{entt::connect_arg<&delegate_functor::identity>, std::as_const(functor)}; + entt::delegate member{entt::connect_arg<&delegate_functor::operator()>, &functor}; + entt::delegate cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)}; ASSERT_TRUE(func); ASSERT_TRUE(member); diff --git a/test/entt/signal/sigh.cpp b/test/entt/signal/sigh.cpp index e994a95b2..dd03a85fe 100644 --- a/test/entt/signal/sigh.cpp +++ b/test/entt/signal/sigh.cpp @@ -165,7 +165,7 @@ TEST_F(SigH, Members) { ASSERT_TRUE(sigh.empty()); ASSERT_EQ(0u, sigh.size()); - sink.connect<&sigh_listener::g>(l1); + sink.connect<&sigh_listener::g>(&l1); sink.connect<&sigh_listener::h>(l2); ASSERT_FALSE(sigh.empty()); @@ -188,7 +188,7 @@ TEST_F(SigH, Collector) { entt::sink sink{sigh}; int cnt = 0; - sink.connect<&sigh_listener::g>(listener); + sink.connect<&sigh_listener::g>(&listener); sink.connect<&sigh_listener::h>(listener); listener.k = true; @@ -218,7 +218,7 @@ TEST_F(SigH, CollectorVoid) { entt::sink sink{sigh}; int cnt = 0; - sink.connect<&sigh_listener::g>(listener); + sink.connect<&sigh_listener::g>(&listener); sink.connect<&sigh_listener::h>(listener); sigh.collect([&cnt]() { ++cnt; }, 42); @@ -330,18 +330,18 @@ TEST_F(SigH, ConstNonConstNoExcept) { const const_nonconst_noexcept cfunctor; sink.connect<&const_nonconst_noexcept::f>(functor); - sink.connect<&const_nonconst_noexcept::g>(functor); + sink.connect<&const_nonconst_noexcept::g>(&functor); sink.connect<&const_nonconst_noexcept::h>(cfunctor); - sink.connect<&const_nonconst_noexcept::i>(cfunctor); + sink.connect<&const_nonconst_noexcept::i>(&cfunctor); sigh.publish(); ASSERT_EQ(functor.cnt, 2); ASSERT_EQ(cfunctor.cnt, 2); sink.disconnect<&const_nonconst_noexcept::f>(functor); - sink.disconnect<&const_nonconst_noexcept::g>(functor); + sink.disconnect<&const_nonconst_noexcept::g>(&functor); sink.disconnect<&const_nonconst_noexcept::h>(cfunctor); - sink.disconnect<&const_nonconst_noexcept::i>(cfunctor); + sink.disconnect<&const_nonconst_noexcept::i>(&cfunctor); sigh.publish(); ASSERT_EQ(functor.cnt, 2); @@ -444,7 +444,7 @@ TEST_F(SigH, BeforeListenerNotPresent) { before_after functor; sink.connect<&before_after::mul>(functor); - sink.before<&before_after::add>(functor).connect<&before_after::add>(functor); + sink.before<&before_after::add>(&functor).connect<&before_after::add>(functor); sigh.publish(2); ASSERT_EQ(functor.value, 2);