delegate/sink: pointer payload

This commit is contained in:
Michele Caini
2019-09-29 23:40:03 +02:00
parent ce4f26a5d5
commit 54f18f3f0d
5 changed files with 146 additions and 31 deletions

1
TODO
View File

@@ -33,4 +33,3 @@
- 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 sigh

View File

@@ -27,8 +27,8 @@ template<typename Ret, typename... Args>
auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<const Payload &, const Type &>>>
auto to_function_pointer(Ret(*)(Type &, Args...), const Payload &) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<const Payload *, const Type *>>>
auto to_function_pointer(Ret(*)(Type &, Args...), const Payload *) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<const Payload *, const Type *>>>
@@ -36,15 +36,15 @@ auto to_function_pointer(Ret(*)(Type *, Args...), const Payload *) -> Ret(*)(Arg
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...);
auto to_function_pointer(Ret(Class:: *)(Args...), const Class *) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...);
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class *) -> Ret(*)(Args...);
template<typename Class, typename Type>
auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)();
auto to_function_pointer(Type Class:: *, const Class *) -> Type(*)();
template<typename... Type>
@@ -171,13 +171,27 @@ 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 object that fits the purpose.
* @param value_or_instance A valid reference 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>(std::forward<Type>(value_or_instance));
connect<Candidate>(value_or_instance);
}
/**
* @brief Constructs a delegate and connects a member for a given instance
* 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 pointer that fits the purpose.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type *value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
}
/**
@@ -203,12 +217,33 @@ 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 object that fits the purpose.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type &&value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<internal::to_function_pointer_t<decltype(Candidate), Type>>;
connect<Candidate>(std::forward<Type>(std::forward<Type>(value_or_instance)), std::make_index_sequence<extent>{});
void connect(Type &value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<internal::to_function_pointer_t<decltype(Candidate), Type *>>;
connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
}
/**
* @brief Connects a member function for a given instance or a free function
* with payload to a delegate.
*
* The delegate 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.<br/>
* 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 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<auto Candidate, typename Type>
void connect(Type *value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<internal::to_function_pointer_t<decltype(Candidate), Type *>>;
connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
}
/**
@@ -310,8 +345,23 @@ 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<internal::to_function_pointer_t<decltype(Candidate), Type>>>;
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::to_function_pointer_t<decltype(Candidate), Type *>>>;
/**
* @brief Deduction guide.
*
* It allows to deduce the function type of the delegate directly from a member
* or a free function with payload provided to the constructor.
*
* @param value_or_instance A valid pointer that fits the purpose.
* @tparam Candidate Member or free function to connect to the delegate.
* @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<internal::to_function_pointer_t<decltype(Candidate), Type *>>>;
}

View File

@@ -305,7 +305,7 @@ class sink<Ret(Args...)> {
using difference_type = typename std::iterator_traits<typename decltype(signal_type::calls)::iterator>::difference_type;
template<auto Candidate, typename Type>
static void release(Type &value_or_instance, void *signal) {
static void release(Type value_or_instance, void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
}
@@ -371,6 +371,27 @@ public:
return other;
}
/**
* @brief Returns a sink that connects before a given member function or
* free function with payload.
* @tparam Candidate Member or free function to look for.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized sink object.
*/
template<auto Candidate, typename Type>
sink before(Type *value_or_instance) {
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
const auto &calls = signal->calls;
const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call));
sink other{*this};
other.offset = std::distance(it, calls.cend());
return other;
}
/**
* @brief Returns a sink that connects before a given instance or specific
* payload.
@@ -461,7 +482,37 @@ public:
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type>>(value_or_instance);
conn.template connect<&release<Candidate, Type &>>(value_or_instance);
return { std::move(conn), signal };
}
/**
* @brief Connects a member function or a free function with payload to a
* signal.
*
* 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.<br/>
* 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 Candidate Member or free function to connect to the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @return A properly initialized connection object.
*/
template<auto Candidate, typename Type>
connection connect(Type *value_or_instance) {
disconnect<Candidate>(value_or_instance);
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
signal->calls.insert(signal->calls.end() - offset, std::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type *>>(value_or_instance);
return { std::move(conn), signal };
}
@@ -492,6 +543,21 @@ public:
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects a member function or a free function with payload from
* a signal.
* @tparam Candidate Member or free function to disconnect from the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
*/
template<auto Candidate, typename Type>
void disconnect(Type *value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> call{};
call.template connect<Candidate>(value_or_instance);
calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end());
}
/**
* @brief Disconnects member functions or free functions based on an
* instance or specific payload.

View File

@@ -212,13 +212,13 @@ TEST(Delegate, DeductionGuide) {
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};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, std::as_const(functor)};
entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)};
entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor};
entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)};
entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, functor};
entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, std::as_const(functor)};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor};
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_with_ref)::function_type, int(int)>);
@@ -330,8 +330,8 @@ TEST(Delegate, VoidVsNonVoidReturnType) {
delegate_functor functor;
entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, std::as_const(functor)};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};
ASSERT_TRUE(func);
ASSERT_TRUE(member);

View File

@@ -165,7 +165,7 @@ TEST_F(SigH, Members) {
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(0u, sigh.size());
sink.connect<&sigh_listener::g>(l1);
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::h>(l2);
ASSERT_FALSE(sigh.empty());
@@ -188,7 +188,7 @@ TEST_F(SigH, Collector) {
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(listener);
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
listener.k = true;
@@ -218,7 +218,7 @@ TEST_F(SigH, CollectorVoid) {
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(listener);
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(listener);
sigh.collect([&cnt]() { ++cnt; }, 42);
@@ -330,18 +330,18 @@ TEST_F(SigH, ConstNonConstNoExcept) {
const const_nonconst_noexcept cfunctor;
sink.connect<&const_nonconst_noexcept::f>(functor);
sink.connect<&const_nonconst_noexcept::g>(functor);
sink.connect<&const_nonconst_noexcept::g>(&functor);
sink.connect<&const_nonconst_noexcept::h>(cfunctor);
sink.connect<&const_nonconst_noexcept::i>(cfunctor);
sink.connect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
sink.disconnect<&const_nonconst_noexcept::f>(functor);
sink.disconnect<&const_nonconst_noexcept::g>(functor);
sink.disconnect<&const_nonconst_noexcept::g>(&functor);
sink.disconnect<&const_nonconst_noexcept::h>(cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(&cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
@@ -444,7 +444,7 @@ TEST_F(SigH, BeforeListenerNotPresent) {
before_after functor;
sink.connect<&before_after::mul>(functor);
sink.before<&before_after::add>(functor).connect<&before_after::add>(functor);
sink.before<&before_after::add>(&functor).connect<&before_after::add>(functor);
sigh.publish(2);
ASSERT_EQ(functor.value, 2);