LiteLoaderBDS-1.16.40/LiteLoader/Header/third-party/entt/signal/dispatcher.hpp
2022-09-21 19:47:03 +08:00

278 lines
8.1 KiB
C++

#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "sigh.hpp"
namespace entt {
/**
* @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 `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.
*/
class dispatcher {
struct basic_pool {
virtual ~basic_pool() = default;
virtual void publish() = 0;
virtual void disconnect(void *) = 0;
virtual void clear() ENTT_NOEXCEPT = 0;
};
template<typename Event>
struct pool_handler final: basic_pool {
static_assert(std::is_same_v<Event, std::decay_t<Event>>, "Invalid event type");
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)...);
}
}
private:
signal_type signal{};
std::vector<Event> events;
};
template<typename Event>
[[nodiscard]] pool_handler<Event> & assure() {
const auto index = type_seq<Event>::value();
if(!(index < pools.size())) {
pools.resize(std::size_t(index)+1u);
}
if(!pools[index]) {
pools[index].reset(new pool_handler<Event>{});
}
return static_cast<pool_handler<Event> &>(*pools[index]);
}
public:
/*! @brief Default constructor. */
dispatcher() = default;
/*! @brief Default move constructor. */
dispatcher(dispatcher &&) = default;
/*! @brief Default move assignment operator. @return This dispatcher. */
dispatcher & operator=(dispatcher &&) = default;
/**
* @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;
};
}
#endif