#ifndef ENTT_SIGNAL_EMITTER_HPP #define ENTT_SIGNAL_EMITTER_HPP #include #include #include #include "../container/dense_map.hpp" #include "../core/compressed_pair.hpp" #include "../core/fwd.hpp" #include "../core/type_info.hpp" #include "../core/utility.hpp" #include "fwd.hpp" namespace entt { /** * @brief General purpose event emitter. * * To create an emitter type, derived classes must inherit from the base as: * * @code{.cpp} * struct my_emitter: emitter { * // ... * } * @endcode * * 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.
* Moreover, whenever an event is published, an emitter also passes a reference * to itself to its listeners. * * @tparam Derived Emitter type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class emitter { using key_type = id_type; using mapped_type = std::function; using alloc_traits = std::allocator_traits; using container_allocator = typename alloc_traits::template rebind_alloc>; using container_type = dense_map, container_allocator>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Default constructor. */ emitter() : emitter{allocator_type{}} {} /** * @brief Constructs an emitter with a given allocator. * @param allocator The allocator to use. */ explicit emitter(const allocator_type &allocator) : handlers{allocator, allocator} {} /*! @brief Default destructor. */ virtual ~emitter() noexcept { static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); } /** * @brief Move constructor. * @param other The instance to move from. */ emitter(emitter &&other) noexcept : handlers{std::move(other.handlers)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ emitter(emitter &&other, const allocator_type &allocator) noexcept : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This dispatcher. */ emitter &operator=(emitter &&other) noexcept { handlers = std::move(other.handlers); return *this; } /** * @brief Exchanges the contents with those of a given emitter. * @param other Emitter to exchange the content with. */ void swap(emitter &other) { using std::swap; swap(handlers, other.handlers); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return handlers.second(); } /** * @brief Publishes a given event. * @tparam Type Type of event to trigger. * @param value An instance of the given type of event. */ template void publish(Type &&value) { if(const auto id = type_id().hash(); handlers.first().contains(id)) { handlers.first()[id](&value); } } /** * @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 void on(std::function func) { handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { func(*static_cast(value), static_cast(*this)); }); } /** * @brief Disconnects a listener from the event emitter. * @tparam Type Type of event of the listener. */ template void erase() { handlers.first().erase(type_hash>>::value()); } /*! @brief Disconnects all the listeners. */ void clear() noexcept { handlers.first().clear(); } /** * @brief Checks if there are listeners registered for the specific event. * @tparam Type Type of event to test. * @return True if there are no listeners registered, false otherwise. */ template [[nodiscard]] bool contains() const { return handlers.first().contains(type_hash>>::value()); } /** * @brief Checks if there are listeners registered with the event emitter. * @return True if there are no listeners registered, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handlers.first().empty(); } private: compressed_pair handlers; }; } // namespace entt #endif