|
|
@@ -2,6 +2,8 @@
|
|
|
#define ENTT_SIGNAL_DELEGATE_HPP
|
|
|
|
|
|
|
|
|
+#include <cassert>
|
|
|
+#include <algorithm>
|
|
|
#include <functional>
|
|
|
#include <type_traits>
|
|
|
#include "../config/config.h"
|
|
|
@@ -65,21 +67,22 @@ class delegate;
|
|
|
|
|
|
|
|
|
/**
|
|
|
- * @brief Utility class to send around functions and member functions.
|
|
|
+ * @brief Utility class to use to send around functions and member functions.
|
|
|
*
|
|
|
* Unmanaged delegate for function pointers and member functions. 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 and member functions provided along with an instance on which
|
|
|
- * to invoke them.
|
|
|
+ * to invoke them. It comes also with limited support for curried functions.
|
|
|
*
|
|
|
* @tparam Ret Return type of a function type.
|
|
|
* @tparam Args Types of arguments of a function type.
|
|
|
*/
|
|
|
template<typename Ret, typename... Args>
|
|
|
class delegate<Ret(Args...)> final {
|
|
|
- using proto_fn_type = Ret(const void *, Args...);
|
|
|
+ using storage_type = std::aligned_storage_t<sizeof(void *), alignof(void *)>;
|
|
|
+ using proto_fn_type = Ret(storage_type &, Args...);
|
|
|
|
|
|
public:
|
|
|
/*! @brief Function type of the delegate. */
|
|
|
@@ -87,11 +90,13 @@ public:
|
|
|
|
|
|
/*! @brief Default constructor. */
|
|
|
delegate() ENTT_NOEXCEPT
|
|
|
- : fn{nullptr}, ref{nullptr}
|
|
|
- {}
|
|
|
+ : storage{}, fn{nullptr}
|
|
|
+ {
|
|
|
+ new (&storage) void *{nullptr};
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * @brief Constructs a delegate and binds a free function to it.
|
|
|
+ * @brief Constructs a delegate and connects a free function to it.
|
|
|
* @tparam Function A valid free function pointer.
|
|
|
*/
|
|
|
template<auto Function>
|
|
|
@@ -102,12 +107,12 @@ public:
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Constructs a delegate and binds a member function to it.
|
|
|
+ * @brief Constructs a delegate and connects a member function to it.
|
|
|
* @tparam Member Member function to connect to the delegate.
|
|
|
* @tparam Class Type of class to which the member function belongs.
|
|
|
* @param instance A valid instance of type pointer to `Class`.
|
|
|
*/
|
|
|
- template<auto Member, typename Class>
|
|
|
+ template<auto Member, typename Class, typename = std::enable_if_t<std::is_member_function_pointer_v<decltype(Member)>>>
|
|
|
delegate(connect_arg_t<Member>, Class *instance) ENTT_NOEXCEPT
|
|
|
: delegate{}
|
|
|
{
|
|
|
@@ -115,60 +120,73 @@ public:
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Binds a free function to a delegate.
|
|
|
+ * @brief Connects a free function to a delegate.
|
|
|
* @tparam Function A valid free function pointer.
|
|
|
*/
|
|
|
template<auto Function>
|
|
|
void connect() ENTT_NOEXCEPT {
|
|
|
static_assert(std::is_invocable_r_v<Ret, decltype(Function), Args...>);
|
|
|
- ref = nullptr;
|
|
|
+ new (&storage) void *{nullptr};
|
|
|
|
|
|
- fn = [](const void *, Args... args) -> Ret {
|
|
|
+ fn = [](storage_type &, Args... args) -> Ret {
|
|
|
return std::invoke(Function, args...);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Connects a member function for a given instance to a delegate.
|
|
|
+ * @brief Connects a member function for a given instance or a curried free
|
|
|
+ * function to a delegate.
|
|
|
*
|
|
|
- * The delegate isn't responsible for the connected object. Users must
|
|
|
- * guarantee that the lifetime of the instance overcomes the one of the
|
|
|
- * delegate.
|
|
|
+ * When used to connect a member function, the delegate isn't responsible
|
|
|
+ * for the connected object. Users must guarantee that the lifetime of the
|
|
|
+ * instance overcomes the one of the delegate.<br/>
|
|
|
+ * When used to connect a curried free function, the linked value must be at
|
|
|
+ * least copyable and such that its size is lower than or equal to the one
|
|
|
+ * of a `void *`. It means that all primitive types are accepted as well as
|
|
|
+ * pointers. Moreover, the signature of the free function must be such that
|
|
|
+ * the value is the first argument before the ones used to define the
|
|
|
+ * delegate itself.
|
|
|
*
|
|
|
- * @tparam Member Member function to connect to the delegate.
|
|
|
- * @tparam Class Type of class to which the member function belongs.
|
|
|
- * @param instance A valid instance of type pointer to `Class`.
|
|
|
+ * @tparam Candidate Member function or curried free function to connect to
|
|
|
+ * the delegate.
|
|
|
+ * @tparam Type Type of class to which the member function belongs or type
|
|
|
+ * of value used for currying.
|
|
|
+ * @param value_or_instance A valid pointer to an instance of class type or
|
|
|
+ * the value to use for currying.
|
|
|
*/
|
|
|
- template<auto Member, typename Class>
|
|
|
- void connect(Class *instance) ENTT_NOEXCEPT {
|
|
|
- static_assert(std::is_invocable_r_v<Ret, decltype(Member), Class, Args...>);
|
|
|
- ref = instance;
|
|
|
-
|
|
|
- fn = [](const void *instance, Args... args) -> Ret {
|
|
|
- if constexpr(std::is_const_v<Class>) {
|
|
|
- return std::invoke(Member, static_cast<Class *>(instance), args...);
|
|
|
- } else {
|
|
|
- return std::invoke(Member, static_cast<Class *>(const_cast<void *>(instance)), args...);
|
|
|
- }
|
|
|
+ template<auto Candidate, typename Type>
|
|
|
+ void connect(Type value_or_instance) ENTT_NOEXCEPT {
|
|
|
+ static_assert(sizeof(Type) <= sizeof(void *));
|
|
|
+ static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type, Args...>);
|
|
|
+ new (&storage) Type{value_or_instance};
|
|
|
+
|
|
|
+ fn = [](storage_type &storage, Args... args) -> Ret {
|
|
|
+ Type value_or_instance = *reinterpret_cast<Type *>(&storage);
|
|
|
+ return std::invoke(Candidate, value_or_instance, args...);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Resets a delegate.
|
|
|
*
|
|
|
- * After a reset, a delegate can be safely invoked with no effect.
|
|
|
+ * After a reset, a delegate cannot be invoked anymore.
|
|
|
*/
|
|
|
void reset() ENTT_NOEXCEPT {
|
|
|
- ref = nullptr;
|
|
|
+ new (&storage) void *{nullptr};
|
|
|
fn = nullptr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Returns the instance bound to a delegate, if any.
|
|
|
- * @return An opaque pointer to the instance bound to the delegate, if any.
|
|
|
+ * @brief Returns the instance linked to a delegate, if any.
|
|
|
+ *
|
|
|
+ * @warning
|
|
|
+ * Attempting to use an instance returned by a delegate that doesn't contain
|
|
|
+ * a pointer to a member function results in undefined behavior.
|
|
|
+ *
|
|
|
+ * @return An opaque pointer to the instance linked to the delegate, if any.
|
|
|
*/
|
|
|
const void * instance() const ENTT_NOEXCEPT {
|
|
|
- return ref;
|
|
|
+ return *reinterpret_cast<const void **>(&storage);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -186,7 +204,8 @@ public:
|
|
|
* @return The value returned by the underlying function.
|
|
|
*/
|
|
|
Ret operator()(Args... args) const {
|
|
|
- return fn(ref, args...);
|
|
|
+ assert(fn);
|
|
|
+ return fn(storage, args...);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -200,27 +219,23 @@ public:
|
|
|
|
|
|
/**
|
|
|
* @brief Checks if the contents of the two delegates are different.
|
|
|
- *
|
|
|
- * Two delegates are identical if they contain the same listener.
|
|
|
- *
|
|
|
* @param other Delegate with which to compare.
|
|
|
* @return True if the two delegates are identical, false otherwise.
|
|
|
*/
|
|
|
bool operator==(const delegate<Ret(Args...)> &other) const ENTT_NOEXCEPT {
|
|
|
- return ref == other.ref && fn == other.fn;
|
|
|
+ const char *lhs = reinterpret_cast<const char *>(&storage);
|
|
|
+ const char *rhs = reinterpret_cast<const char *>(&other.storage);
|
|
|
+ return fn == other.fn && std::equal(lhs, lhs + sizeof(storage_type), rhs);
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
+ mutable storage_type storage;
|
|
|
proto_fn_type *fn;
|
|
|
- const void *ref;
|
|
|
};
|
|
|
|
|
|
|
|
|
/**
|
|
|
* @brief Checks if the contents of the two delegates are different.
|
|
|
- *
|
|
|
- * Two delegates are identical if they contain the same listener.
|
|
|
- *
|
|
|
* @tparam Ret Return type of a function type.
|
|
|
* @tparam Args Types of arguments of a function type.
|
|
|
* @param lhs A valid delegate object.
|