Class dispatcher

Synopsis

#include <src/entt/signal/dispatcher.hpp>

class dispatcher

Description

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.
Listeners are provided in the form of member functions. For each event of type Event, listeners are such that they can be invoked with an argument of type Event &, 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.

Mentioned in

Methods

clearDiscards all the events queued so far.
disconnectUtility function to disconnect everything related to a given value or instance from a dispatcher.
enqueueEnqueues an event of the given type.
sinkReturns a sink object for the given event.
triggerTriggers an immediate event of the given type.
updateDelivers all the pending events of the given type.

Source

Lines 31-272 in src/entt/signal/dispatcher.hpp.

class dispatcher {
    struct basic_pool {
        virtual ~basic_pool() = default;
        virtual void publish() = 0;
        virtual void disconnect(void *) = 0;
        virtual void clear() ENTT_NOEXCEPT = 0;
        [[nodiscard]] virtual id_type type_id() const ENTT_NOEXCEPT = 0;
    };

    template<typename Event>
    struct pool_handler final: basic_pool {
        using signal_type = sigh<void(Event &)>;
        using sink_type = typename signal_type::sink_type;

        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 {
            sink().disconnect(instance);
        }

        void clear() ENTT_NOEXCEPT override {
            events.clear();
        }

        [[nodiscard]] sink_type sink() ENTT_NOEXCEPT {
            return entt::sink{signal};
        }

        template<typename... Args>
        void trigger(Args &&... args) {
            Event instance{std::forward<Args>(args)...};
            signal.publish(instance);
        }

        template<typename... Args>
        void enqueue(Args &&... args) {
            if constexpr(std::is_aggregate_v<Event>) {
                events.push_back(Event{std::forward<Args>(args)...});
            } else {
                events.emplace_back(std::forward<Args>(args)...);
            }
        }

        [[nodiscard]] id_type type_id() const ENTT_NOEXCEPT override {
            return type_info<Event>::id();
        }

    private:
        signal_type signal{};
        std::vector<Event> events;
    };

    template<typename Event>
    [[nodiscard]] pool_handler<Event> & assure() {
        static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");

        if constexpr(ENTT_FAST_PATH(has_type_index_v<Event>)) {
            const auto index = type_index<Event>::value();

            if(!(index < pools.size())) {
                pools.resize(index+1u);
            }

            if(!pools[index]) {
                pools[index].reset(new pool_handler<Event>{});
            }

            return static_cast<pool_handler<Event> &>(*pools[index]);
        } else {
            auto it = std::find_if(pools.begin(), pools.end(), [id = type_info<Event>::id()](const auto &cpool) { return id == cpool->type_id(); });
            return static_cast<pool_handler<Event> &>(it == pools.cend() ? *pools.emplace_back(new pool_handler<Event>{}) : **it);
        }
    }

public:
    /**
     * @brief Returns a sink object for the given event.
     *
     * A sink is an opaque object used to connect listeners to events.
     *
     * The function type for a listener is _compatible_ with:
     * @code{.cpp}
     * void(Event &);
     * @endcode
     *
     * The order of invocation of the listeners isn't guaranteed.
     *
     * @sa sink
     *
     * @tparam Event Type of event of which to get the sink.
     * @return A temporary sink object.
     */
    template<typename Event>
    [[nodiscard]] auto sink() {
        return assure<Event>().sink();
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @tparam Args Types of arguments to use to construct the event.
     * @param args Arguments to use to construct the event.
     */
    template<typename Event, typename... Args>
    void trigger(Args &&... args) {
        assure<Event>().trigger(std::forward<Args>(args)...);
    }

    /**
     * @brief Triggers an immediate event of the given type.
     *
     * All the listeners registered for the given type are immediately notified.
     * The event is discarded after the execution.
     *
     * @tparam Event Type of event to trigger.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void trigger(Event &&event) {
        assure<std::decay_t<Event>>().trigger(std::forward<Event>(event));
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event 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 Event, typename... Args>
    void enqueue(Args &&... args) {
        assure<Event>().enqueue(std::forward<Args>(args)...);
    }

    /**
     * @brief Enqueues an event of the given type.
     *
     * An event of the given type is queued. No listener is invoked. Use the
     * `update` member function to notify listeners when ready.
     *
     * @tparam Event Type of event to enqueue.
     * @param event An instance of the given type of event.
     */
    template<typename Event>
    void enqueue(Event &&event) {
        assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
    }

    /**
     * @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) {
            if(cpool) {
                cpool->disconnect(value_or_instance);
            }
        }
    }

    /**
     * @brief Discards all the events queued so far.
     *
     * If no types are provided, the dispatcher will clear all the existing
     * pools.
     *
     * @tparam Event Type of events to discard.
     */
    template<typename... Event>
    void clear() {
        if constexpr(sizeof...(Event) == 0) {
            for(auto &&cpool: pools) {
                if(cpool) {
                    cpool->clear();
                }
            }
        } else {
            (assure<Event>().clear(), ...);
        }
    }

    /**
     * @brief Delivers all the pending events of the given type.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     *
     * @tparam Event Type of events to send.
     */
    template<typename Event>
    void update() {
        assure<Event>().publish();
    }

    /**
     * @brief Delivers all the pending events.
     *
     * This method is blocking and it doesn't return until all the events are
     * delivered to the registered listeners. It's responsibility of the users
     * to reduce at a minimum the time spent in the bodies of the listeners.
     */
    void update() const {
        for(auto pos = pools.size(); pos; --pos) {
            if(auto &&cpool = pools[pos-1]; cpool) {
                cpool->publish();
            }
        }
    }

private:
    std::vector<std::unique_ptr<basic_pool>> pools;
};





Add Discussion

Log in to comment