delegate: support for unbound members

This commit is contained in:
Michele Caini
2020-01-19 15:16:38 +01:00
parent 5d1802faf7
commit 409d5f5966
2 changed files with 64 additions and 104 deletions

View File

@@ -24,36 +24,32 @@ namespace internal {
template<typename Ret, typename... Args>
auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
auto 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 Type, typename... Args, typename Other>
auto function_pointer(Ret(*)(Type, Args...), Other &&) -> 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 Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...), const Class *) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... 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(*)();
template<typename Class, typename Type, typename... Other>
auto function_pointer(Type Class:: *, Other &&...) -> Type(*)();
template<typename... Type>
using to_function_pointer_t = decltype(internal::to_function_pointer(std::declval<Type>()...));
using function_pointer_t = decltype(internal::function_pointer(std::declval<Type>()...));
template<typename Ret, typename... Args>
template<typename... Class, typename Ret, typename... Args>
constexpr auto index_sequence_for(Ret(*)(Args...)) {
return std::index_sequence_for<Args...>{};
return std::index_sequence_for<Class..., Args...>{};
}
@@ -92,9 +88,8 @@ class delegate;
* Unmanaged delegate for function pointers and members. Users of this class are
* in charge of disconnecting instances before deleting them.
*
* A delegate can be used as general purpose invoker with no memory overhead for
* free functions (with or without payload) and members provided along with an
* instance on which to invoke them.
* A delegate can be used as a general purpose invoker without memory overhead
* for free functions possibly with payloads and bound or unbound members.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
@@ -103,14 +98,14 @@ template<typename Ret, typename... Args>
class delegate<Ret(Args...)> {
using proto_fn_type = Ret(const void *, std::tuple<Args &&...>);
template<auto Function, std::size_t... Index>
template<auto Candidate, std::size_t... Index>
void connect(std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert(std::is_invocable_r_v<Ret, decltype(Function), std::tuple_element_t<Index, std::tuple<Args...>>...>);
static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), std::tuple_element_t<Index, std::tuple<Args...>>...>);
data = nullptr;
fn = [](const void *, std::tuple<Args &&...> args) -> Ret {
// Ret(...) makes void(...) eat the return values to avoid errors
return Ret(std::invoke(Function, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
return Ret(std::invoke(Candidate, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
};
}
@@ -148,56 +143,47 @@ public:
{}
/**
* @brief Constructs a delegate and connects a free function to it.
* @tparam Function A valid free function pointer.
* @brief Constructs a delegate and connects a free function or an unbound
* member.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Function>
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
: delegate{}
{
connect<Function>();
connect<Candidate>();
}
/**
* @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.
* @brief Constructs a delegate and connects a free function with payload or
* a bound member.
* @tparam Candidate Function or member 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.
*/
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));
}
/**
* @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.
* @brief Connects a free function or an unbound member to a delegate.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type *value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
}
/**
* @brief Connects a free function to a delegate.
* @tparam Function A valid free function pointer.
*/
template<auto Function>
template<auto Candidate>
void connect() ENTT_NOEXCEPT {
connect<Function>(internal::index_sequence_for(internal::to_function_pointer_t<decltype(Function)>{}));
if constexpr(std::is_member_pointer_v<decltype(Candidate)>) {
connect<Candidate>(internal::index_sequence_for<std::tuple_element_t<0, std::tuple<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
} else {
connect<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
}
}
/**
* @brief Connects a member function for a given instance or a free function
* with payload to a delegate.
* @brief Connects a free function with payload or a bound member 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
@@ -206,33 +192,13 @@ public:
* 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 Candidate Function or member 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.
*/
template<auto Candidate, typename Type>
void connect(Type &value_or_instance) ENTT_NOEXCEPT {
connect<Candidate>(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t<decltype(Candidate), Type *>{}));
}
/**
* @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 {
connect<Candidate>(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t<decltype(Candidate), Type *>{}));
void connect(Type &&value_or_instance) ENTT_NOEXCEPT {
connect<Candidate>(std::forward<Type>(value_or_instance), internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
}
/**
@@ -312,43 +278,21 @@ bool operator!=(const delegate<Ret(Args...)> &lhs, const delegate<Ret(Args...)>
/**
* @brief Deduction guide.
*
* It allows to deduce the function type of the delegate directly from a
* function provided to the constructor.
*
* @tparam Function A valid free function pointer.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Function>
delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::to_function_pointer_t<decltype(Function)>>>;
template<auto Candidate>
delegate(connect_arg_t<Candidate>) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
/**
* @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.
*
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Candidate Function or member 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 &) 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.
*
* @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 *) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::to_function_pointer_t<decltype(Candidate), Type *>>>;
delegate(connect_arg_t<Candidate>, Type &&) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
}

View File

@@ -352,3 +352,19 @@ TEST(Delegate, TheLessTheBetter) {
ASSERT_EQ(delegate(3, 'c'), 6);
}
TEST(Delegate, UnboundDataMember) {
entt::delegate<int(const delegate_functor &)> delegate;
delegate.connect<&delegate_functor::data_member>();
delegate_functor functor;
ASSERT_EQ(delegate(functor), 42);
}
TEST(Delegate, UnboundMemberFunction) {
entt::delegate<int(delegate_functor *, const int &i)> delegate;
delegate.connect<&delegate_functor::operator()>();
delegate_functor functor;
ASSERT_EQ(delegate(&functor, 3), 6);
}