delegate: built-in support for pointer payload
This commit is contained in:
2
TODO
2
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
|
||||
|
||||
@@ -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,19 +124,24 @@ class delegate<Ret(Args...)> {
|
||||
data = &value_or_instance;
|
||||
|
||||
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
|
||||
Type *curr = nullptr;
|
||||
|
||||
if constexpr(std::is_const_v<Type>) {
|
||||
curr = static_cast<Type *>(payload);
|
||||
} else {
|
||||
curr = static_cast<Type *>(const_cast<void *>(payload));
|
||||
}
|
||||
|
||||
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))...));
|
||||
};
|
||||
}
|
||||
|
||||
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))...));
|
||||
};
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Function type of the delegate. */
|
||||
using function_type = Ret(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)))>>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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_function>, 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_function>(value);
|
||||
rhs.connect<&curried_by_ref>(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_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_by_ptr>, &value}));
|
||||
ASSERT_TRUE(lhs != rhs);
|
||||
ASSERT_FALSE(lhs == rhs);
|
||||
ASSERT_NE(lhs, rhs);
|
||||
|
||||
rhs.connect<&curried_by_ptr>(&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>();
|
||||
|
||||
Reference in New Issue
Block a user