Explorar o código

delegate works now also with move only types (close #272)

Michele Caini %!s(int64=6) %!d(string=hai) anos
pai
achega
46150a2da4
Modificáronse 3 ficheiros con 36 adicións e 8 borrados
  1. 1 0
      TODO
  2. 8 8
      src/entt/signal/delegate.hpp
  3. 27 0
      test/entt/signal/delegate.cpp

+ 1 - 0
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

+ 8 - 8
src/entt/signal/delegate.hpp

@@ -101,16 +101,16 @@ class delegate;
  */
 template<typename Ret, typename... Args>
 class delegate<Ret(Args...)> {
-    using proto_fn_type = Ret(const void *, std::tuple<Args...>);
+    using proto_fn_type = Ret(const void *, std::tuple<Args &&...>);
 
     template<auto Function, std::size_t... Index>
     void connect(std::index_sequence<Index...>) ENTT_NOEXCEPT {
         data = nullptr;
 
-        fn = [](const void *, std::tuple<Args...> args) -> Ret {
+        fn = [](const void *, std::tuple<Args &&...> args) -> Ret {
+            // Ret(...) makes void(...) eat the return values to avoid errors
             static_assert(std::is_invocable_r_v<Ret, decltype(Function), std::tuple_element_t<Index, std::tuple<Args...>>...>);
-            // Ret(...) allows void(...) to eat return values and avoid errors
-            return Ret(std::invoke(Function, std::get<Index>(args)...));
+            return Ret(std::invoke(Function, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
         };
     }
 
@@ -118,7 +118,7 @@ class delegate<Ret(Args...)> {
     void connect(Type *value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
         data = value_or_instance;
 
-        fn = [](const void *payload, std::tuple<Args...> args) -> Ret {
+        fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
             Type *curr = nullptr;
 
             if constexpr(std::is_const_v<Type>) {
@@ -127,9 +127,9 @@ class delegate<Ret(Args...)> {
                 curr = static_cast<Type *>(const_cast<void *>(payload));
             }
 
+            // Ret(...) makes void(...) eat the return values to avoid errors
             static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, std::tuple_element_t<Index, std::tuple<Args...>>...>);
-            // Ret(...) allows void(...) to eat return values and avoid errors
-            return Ret(std::invoke(Candidate, curr, std::get<Index>(args)...));
+            return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(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>(args)...));
     }
 
     /**

+ 27 - 0
test/entt/signal/delegate.cpp

@@ -1,3 +1,4 @@
+#include <memory>
 #include <gtest/gtest.h>
 #include <entt/signal/delegate.hpp>
 
@@ -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<int> 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<int(int)>{});
 }
 
+TEST(Delegate, NonConstReference) {
+    entt::delegate<int(int &)> delegate;
+    delegate.connect<&non_const_reference>();
+    int value = 3;
+
+    ASSERT_EQ(delegate(value), value);
+    ASSERT_EQ(value, 9);
+}
+
+TEST(Delegate, MoveOnlyType) {
+    entt::delegate<int(std::unique_ptr<int>)> delegate;
+    auto ptr = std::make_unique<int>(3);
+    delegate.connect<&move_only_type>();
+
+    ASSERT_EQ(delegate(std::move(ptr)), 3);
+    ASSERT_FALSE(ptr);
+}
+
 TEST(Delegate, CurriedFunction) {
     entt::delegate<int(int)> delegate;
     const auto value = 3;