|
|
@@ -1,10 +1,7 @@
|
|
|
#ifndef ENTT_SIGNAL_EMITTER_HPP
|
|
|
#define ENTT_SIGNAL_EMITTER_HPP
|
|
|
|
|
|
-#include <algorithm>
|
|
|
#include <functional>
|
|
|
-#include <iterator>
|
|
|
-#include <list>
|
|
|
#include <memory>
|
|
|
#include <type_traits>
|
|
|
#include <utility>
|
|
|
@@ -20,8 +17,7 @@ namespace entt {
|
|
|
/**
|
|
|
* @brief General purpose event emitter.
|
|
|
*
|
|
|
- * The emitter class template follows the CRTP idiom. To create a custom emitter
|
|
|
- * type, derived classes must inherit directly from the base class as:
|
|
|
+ * To create an emitter type, derived classes must inherit from the base as:
|
|
|
*
|
|
|
* @code{.cpp}
|
|
|
* struct my_emitter: emitter<my_emitter> {
|
|
|
@@ -29,273 +25,99 @@ namespace entt {
|
|
|
* }
|
|
|
* @endcode
|
|
|
*
|
|
|
- * Pools for the type of events are created internally on the fly. It's not
|
|
|
- * required to specify in advance the full list of accepted types.<br/>
|
|
|
- * Moreover, whenever an event is published, an emitter provides the listeners
|
|
|
- * with a reference to itself along with a reference to the event. Therefore
|
|
|
- * listeners have an handy way to work with it without incurring in the need of
|
|
|
- * capturing a reference to the emitter.
|
|
|
+ * Handlers for the different events are created internally on the fly. It's not
|
|
|
+ * required to specify in advance the full list of accepted events.<br/>
|
|
|
+ * Moreover, whenever an event is published, an emitter also passes a reference
|
|
|
+ * to itself to its listeners.
|
|
|
*
|
|
|
- * @tparam Derived Actual type of emitter that extends the class template.
|
|
|
+ * @tparam Derived Emitter type.
|
|
|
*/
|
|
|
template<typename Derived>
|
|
|
class emitter {
|
|
|
- struct basic_pool {
|
|
|
- virtual ~basic_pool() = default;
|
|
|
- virtual bool empty() const ENTT_NOEXCEPT = 0;
|
|
|
- virtual void clear() ENTT_NOEXCEPT = 0;
|
|
|
- };
|
|
|
+ template<typename Type>
|
|
|
+ using function_type = std::function<void(Type &, Derived &)>;
|
|
|
|
|
|
- template<typename Event>
|
|
|
- struct pool_handler final: basic_pool {
|
|
|
- static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
|
|
|
+ template<typename Type>
|
|
|
+ [[nodiscard]] function_type<Type> &assure() {
|
|
|
+ static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
|
|
+ auto &&ptr = handlers[type_hash<Type>::value()];
|
|
|
|
|
|
- using listener_type = std::function<void(Event &, Derived &)>;
|
|
|
- using element_type = std::pair<bool, listener_type>;
|
|
|
- using container_type = std::list<element_type>;
|
|
|
- using connection_type = typename container_type::iterator;
|
|
|
-
|
|
|
- [[nodiscard]] bool empty() const ENTT_NOEXCEPT override {
|
|
|
- auto pred = [](auto &&element) { return element.first; };
|
|
|
-
|
|
|
- return std::all_of(once_list.cbegin(), once_list.cend(), pred)
|
|
|
- && std::all_of(on_list.cbegin(), on_list.cend(), pred);
|
|
|
- }
|
|
|
-
|
|
|
- void clear() ENTT_NOEXCEPT override {
|
|
|
- if(publishing) {
|
|
|
- for(auto &&element: once_list) {
|
|
|
- element.first = true;
|
|
|
- }
|
|
|
-
|
|
|
- for(auto &&element: on_list) {
|
|
|
- element.first = true;
|
|
|
- }
|
|
|
- } else {
|
|
|
- once_list.clear();
|
|
|
- on_list.clear();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- connection_type once(listener_type listener) {
|
|
|
- return once_list.emplace(once_list.cend(), false, std::move(listener));
|
|
|
- }
|
|
|
-
|
|
|
- connection_type on(listener_type listener) {
|
|
|
- return on_list.emplace(on_list.cend(), false, std::move(listener));
|
|
|
- }
|
|
|
-
|
|
|
- void erase(connection_type conn) {
|
|
|
- conn->first = true;
|
|
|
-
|
|
|
- if(!publishing) {
|
|
|
- auto pred = [](auto &&element) { return element.first; };
|
|
|
- once_list.remove_if(pred);
|
|
|
- on_list.remove_if(pred);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void publish(Event &event, Derived &ref) {
|
|
|
- container_type swap_list;
|
|
|
- once_list.swap(swap_list);
|
|
|
-
|
|
|
- publishing = true;
|
|
|
-
|
|
|
- for(auto &&element: on_list) {
|
|
|
- element.first ? void() : element.second(event, ref);
|
|
|
- }
|
|
|
-
|
|
|
- for(auto &&element: swap_list) {
|
|
|
- element.first ? void() : element.second(event, ref);
|
|
|
- }
|
|
|
-
|
|
|
- publishing = false;
|
|
|
-
|
|
|
- on_list.remove_if([](auto &&element) { return element.first; });
|
|
|
+ if(!ptr) {
|
|
|
+ ptr = std::make_shared<function_type<Type>>();
|
|
|
}
|
|
|
|
|
|
- private:
|
|
|
- bool publishing{false};
|
|
|
- container_type once_list{};
|
|
|
- container_type on_list{};
|
|
|
- };
|
|
|
-
|
|
|
- template<typename Event>
|
|
|
- [[nodiscard]] pool_handler<Event> *assure() {
|
|
|
- if(auto &&ptr = pools[type_hash<Event>::value()]; !ptr) {
|
|
|
- auto *cpool = new pool_handler<Event>{};
|
|
|
- ptr.reset(cpool);
|
|
|
- return cpool;
|
|
|
- } else {
|
|
|
- return static_cast<pool_handler<Event> *>(ptr.get());
|
|
|
- }
|
|
|
+ return *static_cast<function_type<Type> *>(ptr.get());
|
|
|
}
|
|
|
|
|
|
- template<typename Event>
|
|
|
- [[nodiscard]] const pool_handler<Event> *assure() const {
|
|
|
- const auto it = pools.find(type_hash<Event>::value());
|
|
|
- return (it == pools.cend()) ? nullptr : static_cast<const pool_handler<Event> *>(it->second.get());
|
|
|
+ template<typename Type>
|
|
|
+ [[nodiscard]] const function_type<Type> *assure() const {
|
|
|
+ const auto it = handlers.find(type_hash<Type>::value());
|
|
|
+ return (it == handlers.cend()) ? nullptr : static_cast<const function_type<Type> *>(it->second.get());
|
|
|
}
|
|
|
|
|
|
public:
|
|
|
- /** @brief Type of listeners accepted for the given event. */
|
|
|
- template<typename Event>
|
|
|
- using listener = typename pool_handler<Event>::listener_type;
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Generic connection type for events.
|
|
|
- *
|
|
|
- * Type of the connection object returned by the event emitter whenever a
|
|
|
- * listener for the given type is registered.<br/>
|
|
|
- * It can be used to break connections still in use.
|
|
|
- *
|
|
|
- * @tparam Event Type of event for which the connection is created.
|
|
|
- */
|
|
|
- template<typename Event>
|
|
|
- struct connection: private pool_handler<Event>::connection_type {
|
|
|
- /** @brief Event emitters are friend classes of connections. */
|
|
|
- friend class emitter;
|
|
|
-
|
|
|
- /*! @brief Default constructor. */
|
|
|
- connection() ENTT_NOEXCEPT = default;
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Creates a connection that wraps its underlying instance.
|
|
|
- * @param conn A connection object to wrap.
|
|
|
- */
|
|
|
- connection(typename pool_handler<Event>::connection_type conn)
|
|
|
- : pool_handler<Event>::connection_type{std::move(conn)} {}
|
|
|
- };
|
|
|
-
|
|
|
/*! @brief Default constructor. */
|
|
|
- emitter() = default;
|
|
|
+ emitter()
|
|
|
+ : handlers{} {}
|
|
|
|
|
|
/*! @brief Default destructor. */
|
|
|
virtual ~emitter() ENTT_NOEXCEPT {
|
|
|
- static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Incorrect use of the class template");
|
|
|
+ static_assert(std::is_base_of_v<emitter<Derived>, Derived>, "Invalid emitter type");
|
|
|
}
|
|
|
|
|
|
/*! @brief Default move constructor. */
|
|
|
emitter(emitter &&) = default;
|
|
|
|
|
|
- /*! @brief Default move assignment operator. @return This emitter. */
|
|
|
- emitter &operator=(emitter &&) = default;
|
|
|
-
|
|
|
/**
|
|
|
- * @brief Emits the given event.
|
|
|
- *
|
|
|
- * All the listeners registered for the specific event type are invoked with
|
|
|
- * the given event. The event type must either have a proper constructor for
|
|
|
- * the arguments provided or be an aggregate type.
|
|
|
- *
|
|
|
- * @tparam Event Type of event to publish.
|
|
|
- * @tparam Args Types of arguments to use to construct the event.
|
|
|
- * @param args Parameters to use to initialize the event.
|
|
|
+ * @brief Default move assignment operator.
|
|
|
+ * @return This emitter.
|
|
|
*/
|
|
|
- template<typename Event, typename... Args>
|
|
|
- void publish(Args &&...args) {
|
|
|
- Event instance{std::forward<Args>(args)...};
|
|
|
- assure<Event>()->publish(instance, *static_cast<Derived *>(this));
|
|
|
- }
|
|
|
+ emitter &operator=(emitter &&) = default;
|
|
|
|
|
|
/**
|
|
|
- * @brief Registers a long-lived listener with the event emitter.
|
|
|
- *
|
|
|
- * This method can be used to register a listener designed to be invoked
|
|
|
- * more than once for the given event type.<br/>
|
|
|
- * The connection returned by the method can be freely discarded. It's meant
|
|
|
- * to be used later to disconnect the listener if required.
|
|
|
- *
|
|
|
- * The listener is as a callable object that can be moved and the type of
|
|
|
- * which is _compatible_ with `void(Event &, Derived &)`.
|
|
|
- *
|
|
|
- * @note
|
|
|
- * Whenever an event is emitted, the emitter provides the listener with a
|
|
|
- * reference to the derived class. Listeners don't have to capture those
|
|
|
- * instances for later uses.
|
|
|
- *
|
|
|
- * @tparam Event Type of event to which to connect the listener.
|
|
|
- * @param instance The listener to register.
|
|
|
- * @return Connection object that can be used to disconnect the listener.
|
|
|
+ * @brief Publishes a given event.
|
|
|
+ * @tparam Type Type of event to trigger.
|
|
|
+ * @param value An instance of the given type of event.
|
|
|
*/
|
|
|
- template<typename Event>
|
|
|
- connection<Event> on(listener<Event> instance) {
|
|
|
- return assure<Event>()->on(std::move(instance));
|
|
|
+ template<typename Type>
|
|
|
+ void publish(Type &&value) {
|
|
|
+ if(auto &handler = assure<std::remove_const_t<std::remove_reference_t<Type>>>(); handler) {
|
|
|
+ handler(value, *static_cast<Derived *>(this));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @brief Registers a short-lived listener with the event emitter.
|
|
|
- *
|
|
|
- * This method can be used to register a listener designed to be invoked
|
|
|
- * only once for the given event type.<br/>
|
|
|
- * The connection returned by the method can be freely discarded. It's meant
|
|
|
- * to be used later to disconnect the listener if required.
|
|
|
- *
|
|
|
- * The listener is as a callable object that can be moved and the type of
|
|
|
- * which is _compatible_ with `void(Event &, Derived &)`.
|
|
|
- *
|
|
|
- * @note
|
|
|
- * Whenever an event is emitted, the emitter provides the listener with a
|
|
|
- * reference to the derived class. Listeners don't have to capture those
|
|
|
- * instances for later uses.
|
|
|
- *
|
|
|
- * @tparam Event Type of event to which to connect the listener.
|
|
|
- * @param instance The listener to register.
|
|
|
- * @return Connection object that can be used to disconnect the listener.
|
|
|
+ * @brief Registers a listener with the event emitter.
|
|
|
+ * @tparam Type Type of event to which to connect the listener.
|
|
|
+ * @param func The listener to register.
|
|
|
*/
|
|
|
- template<typename Event>
|
|
|
- connection<Event> once(listener<Event> instance) {
|
|
|
- return assure<Event>()->once(std::move(instance));
|
|
|
+ template<typename Type>
|
|
|
+ void on(std::function<void(Type &, Derived &)> func) {
|
|
|
+ assure<Type>() = std::move(func);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Disconnects a listener from the event emitter.
|
|
|
- *
|
|
|
- * Do not use twice the same connection to disconnect a listener, it results
|
|
|
- * in undefined behavior. Once used, discard the connection object.
|
|
|
- *
|
|
|
- * @tparam Event Type of event of the connection.
|
|
|
- * @param conn A valid connection.
|
|
|
+ * @tparam Type Type of event of the listener.
|
|
|
*/
|
|
|
- template<typename Event>
|
|
|
- void erase(connection<Event> conn) {
|
|
|
- assure<Event>()->erase(std::move(conn));
|
|
|
+ template<typename Type>
|
|
|
+ void erase() {
|
|
|
+ handlers.erase(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * @brief Disconnects all the listeners for the given event type.
|
|
|
- *
|
|
|
- * All the connections previously returned for the given event are
|
|
|
- * invalidated. Using them results in undefined behavior.
|
|
|
- *
|
|
|
- * @tparam Event Type of event to reset.
|
|
|
- */
|
|
|
- template<typename Event>
|
|
|
- void clear() {
|
|
|
- assure<Event>()->clear();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * @brief Disconnects all the listeners.
|
|
|
- *
|
|
|
- * All the connections previously returned are invalidated. Using them
|
|
|
- * results in undefined behavior.
|
|
|
- */
|
|
|
+ /*! @brief Disconnects all the listeners. */
|
|
|
void clear() ENTT_NOEXCEPT {
|
|
|
- for(auto &&cpool: pools) {
|
|
|
- cpool.second->clear();
|
|
|
- }
|
|
|
+ handlers.clear();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Checks if there are listeners registered for the specific event.
|
|
|
- * @tparam Event Type of event to test.
|
|
|
+ * @tparam Type Type of event to test.
|
|
|
* @return True if there are no listeners registered, false otherwise.
|
|
|
*/
|
|
|
- template<typename Event>
|
|
|
- [[nodiscard]] bool empty() const {
|
|
|
- const auto *cpool = assure<Event>();
|
|
|
- return !cpool || cpool->empty();
|
|
|
+ template<typename Type>
|
|
|
+ [[nodiscard]] bool contains() const {
|
|
|
+ return handlers.contains(type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -303,13 +125,11 @@ public:
|
|
|
* @return True if there are no listeners registered, false otherwise.
|
|
|
*/
|
|
|
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
|
|
- return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) {
|
|
|
- return cpool.second->empty();
|
|
|
- });
|
|
|
+ return handlers.empty();
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
- dense_map<id_type, std::unique_ptr<basic_pool>, identity> pools{};
|
|
|
+ dense_map<id_type, std::shared_ptr<void>, identity> handlers{};
|
|
|
};
|
|
|
|
|
|
} // namespace entt
|