소스 검색

delegate: built-in support for pointer payload

Michele Caini 6 년 전
부모
커밋
f672d734f9
3개의 변경된 파일75개의 추가작업 그리고 33개의 파일을 삭제
  1. 1 1
      TODO
  2. 25 16
      src/entt/signal/delegate.hpp
  3. 49 16
      test/entt/signal/delegate.cpp

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

+ 25 - 16
src/entt/signal/delegate.hpp

@@ -31,6 +31,10 @@ template<typename Ret, typename... Args, typename Type, typename Payload, typena
 auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...);
 
 
+template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<Payload *, Type *>>>
+auto to_function_pointer(Ret(*)(Type *, Args...), Payload *) -> Ret(*)(Args...);
+
+
 template<typename Class, typename Ret, typename... Args>
 auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...);
 
@@ -120,16 +124,21 @@ class delegate<Ret(Args...)> {
         data = &value_or_instance;
 
         fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
-            Type *curr = nullptr;
+            Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
+            // Ret(...) makes void(...) eat the return values to avoid errors
+            return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
+        };
+    }
 
-            if constexpr(std::is_const_v<Type>) {
-                curr = static_cast<Type *>(payload);
-            } else {
-                curr = static_cast<Type *>(const_cast<void *>(payload));
-            }
+    template<auto Candidate, typename Type, std::size_t... Index>
+    void connect(Type *value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
+        static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, std::tuple_element_t<Index, std::tuple<Args...>>...>);
+        data = value_or_instance;
 
+        fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
+            Type *curr = static_cast<Type *>(const_cast<std::conditional_t<std::is_const_v<Type>, const void *, void *>>(payload));
             // Ret(...) makes void(...) eat the return values to avoid errors
-            return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
+            return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(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<auto Candidate, typename Type>
-    delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
+    delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT
         : delegate{}
     {
-        connect<Candidate>(value_or_instance);
+        connect<Candidate>(std::forward<Type>(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<auto Candidate, typename Type>
-    void connect(Type &value_or_instance) ENTT_NOEXCEPT {
-        constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), value_or_instance))>;
-        connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
+    void connect(Type &&value_or_instance) ENTT_NOEXCEPT {
+        constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), std::forward<Type>(value_or_instance)))>;
+        connect<Candidate>(std::forward<Type>(value_or_instance), std::make_index_sequence<extent>{});
     }
 
     /**
@@ -297,8 +306,8 @@ delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
  * @tparam Type Type of class or type of payload.
  */
 template<auto Candidate, typename Type>
-delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
--> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, value_or_instance))>>;
+delegate(connect_arg_t<Candidate>, Type &&value_or_instance) ENTT_NOEXCEPT
+-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, std::forward<Type>(value_or_instance)))>>;
 
 
 }

+ 49 - 16
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<int(int)>{entt::connect_arg<&curried_by_ref>, value}));
+    ASSERT_TRUE(lhs != rhs);
+    ASSERT_FALSE(lhs == rhs);
+    ASSERT_NE(lhs, rhs);
+
+    rhs.connect<&curried_by_ref>(value);
+
+    ASSERT_EQ(rhs, (entt::delegate<int(int)>{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<int(int)>{entt::connect_arg<&curried_function>, value}));
+    ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_by_ptr>, &value}));
     ASSERT_TRUE(lhs != rhs);
     ASSERT_FALSE(lhs == rhs);
     ASSERT_NE(lhs, rhs);
 
-    rhs.connect<&curried_function>(value);
+    rhs.connect<&curried_by_ptr>(&value);
 
-    ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, value}));
+    ASSERT_EQ(rhs, (entt::delegate<int(int)>{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<typename decltype(func)::function_type, int(const int &)>);
-    static_assert(std::is_same_v<typename decltype(curried_func)::function_type, int(int)>);
-    static_assert(std::is_same_v<typename decltype(curried_func_const)::function_type, int(int)>);
+    static_assert(std::is_same_v<typename decltype(curried_func_with_ref)::function_type, int(int)>);
+    static_assert(std::is_same_v<typename decltype(curried_func_with_const_ref)::function_type, int(int)>);
+    static_assert(std::is_same_v<typename decltype(curried_func_with_ptr)::function_type, int(int)>);
+    static_assert(std::is_same_v<typename decltype(curried_func_with_const_ptr)::function_type, int(int)>);
     static_assert(std::is_same_v<typename decltype(member_func_f)::function_type, void()>);
     static_assert(std::is_same_v<typename decltype(member_func_g)::function_type, void()>);
     static_assert(std::is_same_v<typename decltype(member_func_h)::function_type, void()>);
@@ -214,8 +236,10 @@ TEST(Delegate, DeductionGuide) {
     static_assert(std::is_same_v<typename decltype(data_member_v_const)::function_type, const int()>);
 
     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<int(int)> 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<int(int)> empty{};
     entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
-    entt::delegate<int(int)> curr{entt::connect_arg<&curried_function>, value};
+    entt::delegate<int(int)> ref{entt::connect_arg<&curried_by_ref>, value};
+    entt::delegate<int(int)> ptr{entt::connect_arg<&curried_by_ptr>, &value};
     entt::delegate<int(int)> 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<int(int, char)> delegate;
+    delegate_functor functor;
 
     // int delegate_function(const int &);
     delegate.connect<&delegate_function>();