| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- #ifndef ENTT_SIGNAL_DISPATCHER_HPP
- #define ENTT_SIGNAL_DISPATCHER_HPP
- #include <cstddef>
- #include <functional>
- #include <memory>
- #include <type_traits>
- #include <utility>
- #include <vector>
- #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"
- #include "sigh.hpp"
- namespace entt {
- /**
- * @cond TURN_OFF_DOXYGEN
- * Internal details not to be documented.
- */
- namespace internal {
- struct basic_dispatcher_handler {
- virtual ~basic_dispatcher_handler() = default;
- virtual void publish() = 0;
- virtual void disconnect(void *) = 0;
- virtual void clear() noexcept = 0;
- virtual std::size_t size() const noexcept = 0;
- };
- template<typename Type, typename Allocator>
- class dispatcher_handler final: public basic_dispatcher_handler {
- static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type");
- using alloc_traits = std::allocator_traits<Allocator>;
- using signal_type = sigh<void(Type &), Allocator>;
- using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
- public:
- using allocator_type = Allocator;
- dispatcher_handler(const allocator_type &allocator)
- : signal{allocator},
- events{allocator} {}
- void publish() override {
- const auto length = events.size();
- for(std::size_t pos{}; pos < length; ++pos) {
- signal.publish(events[pos]);
- }
- events.erase(events.cbegin(), events.cbegin() + length);
- }
- void disconnect(void *instance) override {
- bucket().disconnect(instance);
- }
- void clear() noexcept override {
- events.clear();
- }
- [[nodiscard]] auto bucket() noexcept {
- return typename signal_type::sink_type{signal};
- }
- void trigger(Type event) {
- signal.publish(event);
- }
- template<typename... Args>
- void enqueue(Args &&...args) {
- if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
- events.push_back(Type{std::forward<Args>(args)...});
- } else {
- events.emplace_back(std::forward<Args>(args)...);
- }
- }
- std::size_t size() const noexcept override {
- return events.size();
- }
- private:
- signal_type signal;
- container_type events;
- };
- } // namespace internal
- /**
- * Internal details not to be documented.
- * @endcond
- */
- /**
- * @brief Basic dispatcher implementation.
- *
- * A dispatcher can be used either to trigger an immediate event or to enqueue
- * events to be published all together once per tick.<br/>
- * Listeners are provided in the form of member functions. For each event of
- * type `Type`, listeners are such that they can be invoked with an argument of
- * type `Type &`, no matter what the return type is.
- *
- * The dispatcher creates instances of the `sigh` class internally. Refer to the
- * documentation of the latter for more details.
- *
- * @tparam Allocator Type of allocator used to manage memory and elements.
- */
- template<typename Allocator>
- class basic_dispatcher {
- template<typename Type>
- using handler_type = internal::dispatcher_handler<Type, Allocator>;
- using key_type = id_type;
- // std::shared_ptr because of its type erased allocator which is useful here
- using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
- using alloc_traits = std::allocator_traits<Allocator>;
- using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
- using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
- template<typename Type>
- [[nodiscard]] handler_type<Type> &assure(const id_type id) {
- static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
- auto &&ptr = pools.first()[id];
- if(!ptr) {
- const auto &allocator = get_allocator();
- ptr = std::allocate_shared<handler_type<Type>>(allocator, allocator);
- }
- return static_cast<handler_type<Type> &>(*ptr);
- }
- template<typename Type>
- [[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
- static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
- if(auto it = pools.first().find(id); it != pools.first().cend()) {
- return static_cast<const handler_type<Type> *>(it->second.get());
- }
- return nullptr;
- }
- public:
- /*! @brief Allocator type. */
- using allocator_type = Allocator;
- /*! @brief Unsigned integer type. */
- using size_type = std::size_t;
- /*! @brief Default constructor. */
- basic_dispatcher()
- : basic_dispatcher{allocator_type{}} {}
- /**
- * @brief Constructs a dispatcher with a given allocator.
- * @param allocator The allocator to use.
- */
- explicit basic_dispatcher(const allocator_type &allocator)
- : pools{allocator, allocator} {}
- /**
- * @brief Move constructor.
- * @param other The instance to move from.
- */
- basic_dispatcher(basic_dispatcher &&other) noexcept
- : pools{std::move(other.pools)} {}
- /**
- * @brief Allocator-extended move constructor.
- * @param other The instance to move from.
- * @param allocator The allocator to use.
- */
- basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
- : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
- ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
- }
- /**
- * @brief Move assignment operator.
- * @param other The instance to move from.
- * @return This dispatcher.
- */
- basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
- ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
- pools = std::move(other.pools);
- return *this;
- }
- /**
- * @brief Exchanges the contents with those of a given dispatcher.
- * @param other Dispatcher to exchange the content with.
- */
- void swap(basic_dispatcher &other) {
- using std::swap;
- swap(pools, other.pools);
- }
- /**
- * @brief Returns the associated allocator.
- * @return The associated allocator.
- */
- [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
- return pools.second();
- }
- /**
- * @brief Returns the number of pending events for a given type.
- * @tparam Type Type of event for which to return the count.
- * @param id Name used to map the event queue within the dispatcher.
- * @return The number of pending events for the given type.
- */
- template<typename Type>
- size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
- const auto *cpool = assure<std::decay_t<Type>>(id);
- return cpool ? cpool->size() : 0u;
- }
- /**
- * @brief Returns the total number of pending events.
- * @return The total number of pending events.
- */
- size_type size() const noexcept {
- size_type count{};
- for(auto &&cpool: pools.first()) {
- count += cpool.second->size();
- }
- return count;
- }
- /**
- * @brief Returns a sink object for the given event and queue.
- *
- * A sink is an opaque object used to connect listeners to events.
- *
- * The function type for a listener is _compatible_ with:
- *
- * @code{.cpp}
- * void(Type &);
- * @endcode
- *
- * The order of invocation of the listeners isn't guaranteed.
- *
- * @sa sink
- *
- * @tparam Type Type of event of which to get the sink.
- * @param id Name used to map the event queue within the dispatcher.
- * @return A temporary sink object.
- */
- template<typename Type>
- [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
- return assure<Type>(id).bucket();
- }
- /**
- * @brief Triggers an immediate event of a given type.
- * @tparam Type Type of event to trigger.
- * @param value An instance of the given type of event.
- */
- template<typename Type>
- void trigger(Type &&value = {}) {
- trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
- }
- /**
- * @brief Triggers an immediate event on a queue of a given type.
- * @tparam Type Type of event to trigger.
- * @param value An instance of the given type of event.
- * @param id Name used to map the event queue within the dispatcher.
- */
- template<typename Type>
- void trigger(const id_type id, Type &&value = {}) {
- assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value));
- }
- /**
- * @brief Enqueues an event of the given type.
- * @tparam Type Type of event to enqueue.
- * @tparam Args Types of arguments to use to construct the event.
- * @param args Arguments to use to construct the event.
- */
- template<typename Type, typename... Args>
- void enqueue(Args &&...args) {
- enqueue_hint<Type>(type_hash<Type>::value(), std::forward<Args>(args)...);
- }
- /**
- * @brief Enqueues an event of the given type.
- * @tparam Type Type of event to enqueue.
- * @param value An instance of the given type of event.
- */
- template<typename Type>
- void enqueue(Type &&value) {
- enqueue_hint(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
- }
- /**
- * @brief Enqueues an event of the given type.
- * @tparam Type Type of event to enqueue.
- * @tparam Args Types of arguments to use to construct the event.
- * @param id Name used to map the event queue within the dispatcher.
- * @param args Arguments to use to construct the event.
- */
- template<typename Type, typename... Args>
- void enqueue_hint(const id_type id, Args &&...args) {
- assure<Type>(id).enqueue(std::forward<Args>(args)...);
- }
- /**
- * @brief Enqueues an event of the given type.
- * @tparam Type Type of event to enqueue.
- * @param id Name used to map the event queue within the dispatcher.
- * @param value An instance of the given type of event.
- */
- template<typename Type>
- void enqueue_hint(const id_type id, Type &&value) {
- assure<std::decay_t<Type>>(id).enqueue(std::forward<Type>(value));
- }
- /**
- * @brief Utility function to disconnect everything related to a given value
- * or instance from a dispatcher.
- * @tparam Type Type of class or type of payload.
- * @param value_or_instance A valid object that fits the purpose.
- */
- template<typename Type>
- void disconnect(Type &value_or_instance) {
- disconnect(&value_or_instance);
- }
- /**
- * @brief Utility function to disconnect everything related to a given value
- * or instance from a dispatcher.
- * @tparam Type Type of class or type of payload.
- * @param value_or_instance A valid object that fits the purpose.
- */
- template<typename Type>
- void disconnect(Type *value_or_instance) {
- for(auto &&cpool: pools.first()) {
- cpool.second->disconnect(value_or_instance);
- }
- }
- /**
- * @brief Discards all the events stored so far in a given queue.
- * @tparam Type Type of event to discard.
- * @param id Name used to map the event queue within the dispatcher.
- */
- template<typename Type>
- void clear(const id_type id = type_hash<Type>::value()) {
- assure<Type>(id).clear();
- }
- /*! @brief Discards all the events queued so far. */
- void clear() noexcept {
- for(auto &&cpool: pools.first()) {
- cpool.second->clear();
- }
- }
- /**
- * @brief Delivers all the pending events of a given queue.
- * @tparam Type Type of event to send.
- * @param id Name used to map the event queue within the dispatcher.
- */
- template<typename Type>
- void update(const id_type id = type_hash<Type>::value()) {
- assure<Type>(id).publish();
- }
- /*! @brief Delivers all the pending events. */
- void update() const {
- for(auto &&cpool: pools.first()) {
- cpool.second->publish();
- }
- }
- private:
- compressed_pair<container_type, allocator_type> pools;
- };
- } // namespace entt
- #endif
|