🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

247 lines
7.4 KiB

#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP
#include <vector>
#include <memory>
#include <utility>
#include <type_traits>
#include "../config/config.h"
#include "../core/family.hpp"
#include "../core/type_traits.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 `const Event &`, no matter what the return type is.
*
* The type of the instances is `Class *` (a naked pointer). It means that users
* must guarantee that the lifetimes of the instances overcome the one of the
* dispatcher itself to avoid crashes.
*/
class dispatcher {
using event_family = family<struct internal_dispatcher_event_family>;
template<typename Class, typename Event>
using instance_type = typename sigh<void(const Event &)>::template instance_type<Class>;
struct base_wrapper {
virtual ~base_wrapper() = default;
virtual void publish() = 0;
};
template<typename Event>
struct signal_wrapper: base_wrapper {
using signal_type = sigh<void(const Event &)>;
using sink_type = typename signal_type::sink_type;
void publish() override {
for(const auto &event: events[current]) {
signal.publish(event);
}
events[current++].clear();
current %= std::extent<decltype(events)>::value;
}
inline sink_type sink() ENTT_NOEXCEPT {
return signal.sink();
}
template<typename... Args>
inline void trigger(Args &&... args) {
signal.publish({ std::forward<Args>(args)... });
}
template<typename... Args>
inline void enqueue(Args &&... args) {
events[current].emplace_back(std::forward<Args>(args)...);
}
private:
signal_type signal{};
std::vector<Event> events[2];
int current{};
};
struct wrapper_data {
std::unique_ptr<base_wrapper> wrapper;
ENTT_ID_TYPE runtime_type;
};
template<typename Event>
static auto type() ENTT_NOEXCEPT {
if constexpr(is_named_type_v<Event>) {
return named_type_traits<Event>::value;
} else {
return event_family::type<Event>;
}
}
template<typename Event>
signal_wrapper<Event> & assure() {
const auto wtype = type<Event>();
wrapper_data *wdata = nullptr;
if constexpr(is_named_type_v<Event>) {
const auto it = std::find_if(wrappers.begin(), wrappers.end(), [wtype](const auto &candidate) {
return candidate.wrapper && candidate.runtime_type == wtype;
});
wdata = (it == wrappers.cend() ? &wrappers.emplace_back() : &(*it));
} else {
if(!(wtype < wrappers.size())) {
wrappers.resize(wtype+1);
}
wdata = &wrappers[wtype];
if(wdata->wrapper && wdata->runtime_type != wtype) {
wrappers.emplace_back();
std::swap(wrappers[wtype], wrappers.back());
wdata = &wrappers[wtype];
}
}
if(!wdata->wrapper) {
wdata->wrapper = std::make_unique<signal_wrapper<Event>>();
wdata->runtime_type = wtype;
}
return static_cast<signal_wrapper<Event> &>(*wdata->wrapper);
}
public:
/*! @brief Type of sink for the given event. */
template<typename Event>
using sink_type = typename signal_wrapper<Event>::sink_type;
/**
* @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:
* @code{.cpp}
* void(const 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>
inline sink_type<Event> sink() ENTT_NOEXCEPT {
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>
inline 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>
inline 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>
inline 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>
inline void enqueue(Event &&event) {
assure<std::decay_t<Event>>().enqueue(std::forward<Event>(event));
}
/**
* @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>
inline 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.
*/
inline void update() const {
for(auto pos = wrappers.size(); pos; --pos) {
auto &wdata = wrappers[pos-1];
if(wdata.wrapper) {
wdata.wrapper->publish();
}
}
}
private:
std::vector<wrapper_data> wrappers;
};
}
#endif // ENTT_SIGNAL_DISPATCHER_HPP