From 9fbdbc1844e4948afd5f3facb9dafec24bab4365 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sun, 10 Feb 2019 23:07:17 +0100 Subject: [PATCH] review: delegate/sigh --- docs/signal.md | 224 +++++++++++++++------------------- src/entt/signal/delegate.hpp | 162 ++++++++---------------- src/entt/signal/sigh.hpp | 48 +++++--- test/entt/signal/delegate.cpp | 104 +++++----------- test/entt/signal/sigh.cpp | 11 +- 5 files changed, 220 insertions(+), 329 deletions(-) diff --git a/docs/signal.md b/docs/signal.md index 3c4604754..1a2fbdf86 100644 --- a/docs/signal.md +++ b/docs/signal.md @@ -6,9 +6,8 @@ # Table of Contents * [Introduction](#introduction) -* [Signals](#signals) * [Delegate](#delegate) - * [Currying and free functions](#currying-and-free-functions) +* [Signals](#signals) * [Event dispatcher](#event-dispatcher) * [Event emitter](#event-emitter) delegate()))>>; - - -/** - * @brief Deduction guideline. - * - * It allows to deduce the function type of the delegate directly from a lambda - * or a functor provided to the constructor. - * - * @tparam Invokable Type of lambda or functor to connect. - * @param invokable A valid instance of the given type. - */ -template -delegate(Invokable invokable) ENTT_NOEXCEPT --> delegate>; +delegate(connect_arg_t, Type *) ENTT_NOEXCEPT +-> delegate()))>>; } diff --git a/src/entt/signal/sigh.hpp b/src/entt/signal/sigh.hpp index 1c83d1932..d60b2eea3 100644 --- a/src/entt/signal/sigh.hpp +++ b/src/entt/signal/sigh.hpp @@ -129,6 +129,9 @@ class sink { template friend struct sigh; + template + Type * payload_type(Ret(*)(Type *, Args...)); + sink(std::vector> *calls) ENTT_NOEXCEPT : calls{calls} {} @@ -159,23 +162,31 @@ public: } /** - * @brief Connects a member function to a signal. + * @brief Connects a member function or a free function with payload to a + * signal. * - * The signal isn't responsible for the connected object. Users must - * 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 member function of a given instance. + * 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 Member Member function to connect to the signal. - * @tparam Class Type of class to which the member function belongs. - * @param instance A valid instance of type pointer to `Class`. + * @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(Class *instance) { - static_assert(std::is_member_function_pointer_v); - disconnect(instance); + template + void connect(Type *value_or_instance) { + if constexpr(std::is_member_function_pointer_v) { + disconnect(value_or_instance); + } else { + disconnect(); + } + delegate delegate{}; - delegate.template connect(instance); + delegate.template connect(value_or_instance); calls->emplace_back(std::move(delegate)); } @@ -186,12 +197,19 @@ public: template void disconnect() { delegate delegate{}; - delegate.template connect(); + + if constexpr(std::is_invocable_r_v) { + delegate.template connect(); + } else { + decltype(payload_type(Function)) payload = nullptr; + delegate.template connect(payload); + } + calls->erase(std::remove(calls->begin(), calls->end(), std::move(delegate)), calls->end()); } /** - * @brief Disconnects the given member function from a signal. + * @brief Disconnects a given member function from a signal. * @tparam Member Member function to disconnect from the signal. * @tparam Class Type of class to which the member function belongs. * @param instance A valid instance of type pointer to `Class`. diff --git a/test/entt/signal/delegate.cpp b/test/entt/signal/delegate.cpp index 3ed73792f..49ec432c3 100644 --- a/test/entt/signal/delegate.cpp +++ b/test/entt/signal/delegate.cpp @@ -5,13 +5,8 @@ int delegate_function(const int &i) { return i*i; } -int curried_function_by_value(int i, int j) { - return i+j; -} - -int curried_function_by_ref(int &value) { - value *= 2; - return value; +int curried_function(const int *i, int j) { + return *i+j; } struct delegate_functor { @@ -81,46 +76,18 @@ TEST(Delegate, DataMembers) { ASSERT_EQ(delegate(), 42); } -TEST(Delegate, LambdaAndFunctor) { - entt::delegate non_capturing_delegate; - entt::delegate capturing_delegate; - entt::delegate functor_delegate; - const int value = 5; - - non_capturing_delegate.connect([](int v) { return v*v; }); - capturing_delegate.connect([val = &value](int v) { return v * *val; }); - functor_delegate.connect(delegate_functor{}); - - ASSERT_EQ(non_capturing_delegate(3), 9); - ASSERT_EQ(capturing_delegate(3), 15); - ASSERT_EQ(functor_delegate(3), 6); -} - TEST(Delegate, Comparison) { entt::delegate lhs; entt::delegate rhs; delegate_functor functor; delegate_functor other; + const int value = 0; ASSERT_EQ(lhs, entt::delegate{}); ASSERT_FALSE(lhs != rhs); ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); - lhs.connect(functor); - - ASSERT_EQ(lhs, entt::delegate{functor}); - ASSERT_TRUE(lhs != rhs); - ASSERT_FALSE(lhs == rhs); - ASSERT_NE(lhs, rhs); - - rhs.connect<>(functor); - - ASSERT_EQ(rhs, entt::delegate{functor}); - ASSERT_FALSE(lhs != rhs); - ASSERT_TRUE(lhs == rhs); - ASSERT_EQ(lhs, rhs); - lhs.connect<&delegate_function>(); ASSERT_EQ(lhs, entt::delegate{entt::connect_arg<&delegate_function>}); @@ -135,16 +102,16 @@ TEST(Delegate, Comparison) { ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); - lhs.connect<&curried_function_by_value>(0); + lhs.connect<&curried_function>(&value); - ASSERT_EQ(lhs, (entt::delegate{entt::connect_arg<&curried_function_by_value>, 0})); + ASSERT_EQ(lhs, (entt::delegate{entt::connect_arg<&curried_function>, &value})); ASSERT_TRUE(lhs != rhs); ASSERT_FALSE(lhs == rhs); ASSERT_NE(lhs, rhs); - rhs.connect<&curried_function_by_value>(0); + rhs.connect<&curried_function>(&value); - ASSERT_EQ(rhs, (entt::delegate{entt::connect_arg<&curried_function_by_value>, 0})); + ASSERT_EQ(rhs, (entt::delegate{entt::connect_arg<&curried_function>, &value})); ASSERT_FALSE(lhs != rhs); ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); @@ -180,7 +147,7 @@ TEST(Delegate, Comparison) { rhs.reset(); - ASSERT_EQ(lhs, (entt::delegate{})); + ASSERT_EQ(rhs, (entt::delegate{})); ASSERT_FALSE(lhs != rhs); ASSERT_TRUE(lhs == rhs); ASSERT_EQ(lhs, rhs); @@ -207,32 +174,24 @@ TEST(Delegate, ConstNonConstNoExcept) { TEST(Delegate, DeducedGuidelines) { const_nonconst_noexcept functor; + const int value = 0; - entt::delegate invokable_functor{delegate_functor{}}; - entt::delegate invokable_lambda{[value = 2](double v) { return v * value; }}; entt::delegate func_deduced{entt::connect_arg<&delegate_function>}; - entt::delegate curried_func_by_ref_deduced{entt::connect_arg<&curried_function_by_ref>, 0}; - entt::delegate curried_func_by_value_deduced{entt::connect_arg<&curried_function_by_value>, 0}; + entt::delegate curried_func_deduced{entt::connect_arg<&curried_function>, &value}; entt::delegate member_f_deduced{entt::connect_arg<&const_nonconst_noexcept::f>, &functor}; entt::delegate member_g_deduced{entt::connect_arg<&const_nonconst_noexcept::g>, &functor}; entt::delegate member_h_deduced{entt::connect_arg<&const_nonconst_noexcept::h>, &functor}; entt::delegate member_i_deduced{entt::connect_arg<&const_nonconst_noexcept::i>, &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); - ASSERT_TRUE(invokable_functor); - ASSERT_TRUE(invokable_lambda); ASSERT_TRUE(func_deduced); - ASSERT_TRUE(curried_func_by_ref_deduced); - ASSERT_TRUE(curried_func_by_value_deduced); + ASSERT_TRUE(curried_func_deduced); ASSERT_TRUE(member_f_deduced); ASSERT_TRUE(member_g_deduced); ASSERT_TRUE(member_h_deduced); @@ -256,40 +215,27 @@ TEST(Delegate, ConstInstance) { ASSERT_EQ(delegate, entt::delegate{}); } -TEST(Delegate, CurriedFunctionByValue) { +TEST(Delegate, CurriedFunction) { entt::delegate delegate; - delegate.connect<&curried_function_by_value>(3); + const auto value = 3; + + delegate.connect<&curried_function>(&value); ASSERT_TRUE(delegate); ASSERT_EQ(delegate(1), 4); } -TEST(Delegate, CurriedFunctionByRef) { - entt::delegate delegate; - delegate.connect<&curried_function_by_ref>(2); - - ASSERT_TRUE(delegate); - ASSERT_EQ(delegate(), 4); - ASSERT_EQ(delegate(), 8); -} - TEST(Delegate, Constructors) { delegate_functor functor; + const auto value = 2; + entt::delegate empty{}; - entt::delegate invokable{functor}; - entt::delegate lambda{[value = 5](int v) { return value * v; }}; entt::delegate func{entt::connect_arg<&delegate_function>}; - entt::delegate curr{entt::connect_arg<&curried_function_by_value>, 2}; + entt::delegate curr{entt::connect_arg<&curried_function>, &value}; entt::delegate member{entt::connect_arg<&delegate_functor::operator()>, &functor}; ASSERT_FALSE(empty); - ASSERT_TRUE(invokable); - ASSERT_EQ(6, invokable(3)); - - ASSERT_TRUE(lambda); - ASSERT_EQ(15, lambda(3)); - ASSERT_TRUE(func); ASSERT_EQ(9, func(3)); @@ -299,3 +245,15 @@ TEST(Delegate, Constructors) { ASSERT_TRUE(member); ASSERT_EQ(6, member(3)); } + +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)}; + + ASSERT_TRUE(func); + ASSERT_TRUE(member); + ASSERT_TRUE(cmember); +} diff --git a/test/entt/signal/sigh.cpp b/test/entt/signal/sigh.cpp index b35ddca6b..c87e0d762 100644 --- a/test/entt/signal/sigh.cpp +++ b/test/entt/signal/sigh.cpp @@ -30,7 +30,7 @@ struct test_collect_all { template<> struct test_collect_all { std::vector vec{}; - static void h() {} + static void h(const void *) {} bool operator()() noexcept { return true; } @@ -39,7 +39,7 @@ struct test_collect_all { template struct test_collect_first { std::vector vec{}; - static int f() { return 42; } + static int f(const void *) { return 42; } bool operator()(Ret r) noexcept { vec.push_back(r); return false; @@ -151,8 +151,9 @@ TEST(SigH, Members) { TEST(SigH, Collector) { entt::sigh> sigh_void; + const void *fake_instance = nullptr; - sigh_void.sink().connect<&test_collect_all::h>(); + sigh_void.sink().connect<&test_collect_all::h>(fake_instance); auto collector_void = sigh_void.collect(); ASSERT_FALSE(sigh_void.empty()); @@ -173,8 +174,8 @@ TEST(SigH, Collector) { entt::sigh> sigh_first; - sigh_first.sink().connect<&test_collect_first::f>(); - sigh_first.sink().connect<&test_collect_first::f>(); + sigh_first.sink().connect<&test_collect_first::f>(fake_instance); + sigh_first.sink().connect<&test_collect_first::f>(fake_instance); auto collector_first = sigh_first.collect(); ASSERT_FALSE(sigh_first.empty());