From 46150a2da470b1f4915447cd7e7e53672cd6e805 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 2 Jul 2019 14:45:41 +0200 Subject: [PATCH] delegate works now also with move only types (close #272) --- TODO | 1 + src/entt/signal/delegate.hpp | 16 ++++++++-------- test/entt/signal/delegate.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 05683a853..005e5e41b 100644 --- a/TODO +++ b/TODO @@ -23,6 +23,7 @@ - iterator based each with a couple of iterators passed from outside (use bitmask + has) * stable component handle that isn't affected by reallocations * multi component registry::remove and some others? +* can I use opaque connection also for the emitter? * built-in support for dual (or N-) buffering TODO diff --git a/src/entt/signal/delegate.hpp b/src/entt/signal/delegate.hpp index 877964b2c..94d675226 100644 --- a/src/entt/signal/delegate.hpp +++ b/src/entt/signal/delegate.hpp @@ -101,16 +101,16 @@ class delegate; */ template class delegate { - using proto_fn_type = Ret(const void *, std::tuple); + using proto_fn_type = Ret(const void *, std::tuple); template void connect(std::index_sequence) ENTT_NOEXCEPT { data = nullptr; - fn = [](const void *, std::tuple args) -> Ret { + fn = [](const void *, std::tuple args) -> Ret { + // Ret(...) makes void(...) eat the return values to avoid errors static_assert(std::is_invocable_r_v>...>); - // Ret(...) allows void(...) to eat return values and avoid errors - return Ret(std::invoke(Function, std::get(args)...)); + return Ret(std::invoke(Function, std::forward>>(std::get(args))...)); }; } @@ -118,7 +118,7 @@ class delegate { void connect(Type *value_or_instance, std::index_sequence) ENTT_NOEXCEPT { data = value_or_instance; - fn = [](const void *payload, std::tuple args) -> Ret { + fn = [](const void *payload, std::tuple args) -> Ret { Type *curr = nullptr; if constexpr(std::is_const_v) { @@ -127,9 +127,9 @@ class delegate { curr = static_cast(const_cast(payload)); } + // Ret(...) makes void(...) eat the return values to avoid errors static_assert(std::is_invocable_r_v>...>); - // Ret(...) allows void(...) to eat return values and avoid errors - return Ret(std::invoke(Candidate, curr, std::get(args)...)); + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(args))...)); }; } @@ -232,7 +232,7 @@ public: */ Ret operator()(Args... args) const { ENTT_ASSERT(fn); - return fn(data, std::forward_as_tuple(args...)); + return fn(data, std::forward_as_tuple(std::forward(args)...)); } /** diff --git a/test/entt/signal/delegate.cpp b/test/entt/signal/delegate.cpp index a44c1d46d..3b63f3915 100644 --- a/test/entt/signal/delegate.cpp +++ b/test/entt/signal/delegate.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -9,6 +10,14 @@ int curried_function(const int *i, int j) { return *i+j; } +int non_const_reference(int &i) { + return i *= i; +} + +int move_only_type(std::unique_ptr ptr) { + return *ptr; +} + struct delegate_functor { int operator()(int i) { return i+i; @@ -235,6 +244,24 @@ TEST(Delegate, ConstInstance) { ASSERT_EQ(delegate, entt::delegate{}); } +TEST(Delegate, NonConstReference) { + entt::delegate delegate; + delegate.connect<&non_const_reference>(); + int value = 3; + + ASSERT_EQ(delegate(value), value); + ASSERT_EQ(value, 9); +} + +TEST(Delegate, MoveOnlyType) { + entt::delegate)> delegate; + auto ptr = std::make_unique(3); + delegate.connect<&move_only_type>(); + + ASSERT_EQ(delegate(std::move(ptr)), 3); + ASSERT_FALSE(ptr); +} + TEST(Delegate, CurriedFunction) { entt::delegate delegate; const auto value = 3;