added support for extra arguments to dispatcher

This commit is contained in:
Michele Caini
2018-12-28 16:10:09 +01:00
parent 397a04b49c
commit d119032cb9
3 changed files with 60 additions and 46 deletions

12
TODO
View File

@@ -8,7 +8,6 @@
* define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
* runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html
* is it possible to iterate all the components assigned to an entity through a common base class?
* can we do more for shared libraries? who knows... see #144
* work stealing job system (see #100)
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
* meta: move-to-head optimization when searching by name on parts (data, func, etc)
@@ -16,10 +15,17 @@
* destroy overload that accepts a couple of iterators (see create)
* allow for built-in parallel each if possible
* add on-the-fly sort functionality (is it possible?)
* write/show how to create an archetype based model on top of EnTT
* write/show how to create prefabs on top of EnTT (that's easy!!)
* mention hunter in the readme file, section packaging tools
* travis + windows is now available, try it
* events on replace, so that one can track updated components? indagate impact
* optimize for empty component - maybe it's possible with a raw-less view that uses fake iterators (with correct size (correct size, same component)
* tags revenge: if it's possible, reintroduce them but without a link to entities (see #169 for more details)
Required test:
* can we do more for shared libraries? who knows... see #144
* type_info::hash_code can be used in place of family when it comes to working with dlls
Ready to go:
* stealing archetype-like model on top of EnTT (design completed, wip dev)
* write/show how to create an archetype based model on top of EnTT
* do not include it in EnTT, it breaks the pay-for-what-you-use principle all the ways

View File

@@ -5,7 +5,6 @@
#include <vector>
#include <memory>
#include <utility>
#include <algorithm>
#include <type_traits>
#include "../config/config.h"
#include "../core/family.hpp"
@@ -22,13 +21,17 @@ namespace entt {
* 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.
* type `const Event &` plus an extra list of arguments to forward with the
* event itself, no matter what the return type is.
*
* Member functions named `receive` are automatically detected and registered or
* unregistered by the dispatcher. 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.
*
* @tparam Args Types of arguments to forward along with an event.
*/
template<typename... Args>
class dispatcher final {
using event_family = family<struct internal_dispatcher_event_family>;
@@ -37,36 +40,38 @@ class dispatcher final {
struct base_wrapper {
virtual ~base_wrapper() = default;
virtual void publish() = 0;
virtual void publish(Args...) = 0;
};
template<typename Event>
struct signal_wrapper final: base_wrapper {
using sink_type = typename sigh<void(const Event &)>::sink_type;
using signal_type = sigh<void(const Event &, Args...)>;
using sink_type = typename signal_type::sink_type;
void publish() override {
const auto &curr = current++;
current %= std::extent<decltype(events)>::value;
std::for_each(events[curr].cbegin(), events[curr].cend(), [this](const auto &event) { signal.publish(event); });
events[curr].clear();
void publish(Args... args) override {
for(const auto &event: events[current]) {
signal.publish(event, args...);
}
events[current].clear();
current = (current + 1) % 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)... });
inline void trigger(const Event &event, Args... args) {
signal.publish(event, args...);
}
template<typename... Args>
inline void enqueue(Args &&... args) {
events[current].push_back({ std::forward<Args>(args)... });
template<typename... Params>
inline void enqueue(Params &&... params) {
events[current].emplace_back(std::forward<Params>(params)...);
}
private:
sigh<void(const Event &)> signal{};
signal_type signal{};
std::vector<Event> events[2];
int current{};
};
@@ -120,12 +125,12 @@ public:
* 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.
* @param event An instance of the given type of event.
* @param args Arguments to forward along with the event.
*/
template<typename Event, typename... Args>
inline void trigger(Args &&... args) {
wrapper<Event>().trigger(std::forward<Args>(args)...);
template<typename Event>
inline void trigger(Event &&event, Args... args) {
wrapper<std::decay_t<Event>>().trigger(std::forward<Event>(event), args...);
}
/**
@@ -135,11 +140,11 @@ public:
* The event is discarded after the execution.
*
* @tparam Event Type of event to trigger.
* @param event An instance of the given type of event.
* @param args Arguments to forward along with the event.
*/
template<typename Event>
inline void trigger(Event &&event) {
wrapper<std::decay_t<Event>>().trigger(std::forward<Event>(event));
inline void trigger(Args... args) {
wrapper<std::decay_t<Event>>().trigger(Event{}, args...);
}
/**
@@ -149,12 +154,12 @@ public:
* `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.
* @tparam Params Types of arguments to use to construct the event.
* @param params Arguments to use to construct the event.
*/
template<typename Event, typename... Args>
inline void enqueue(Args &&... args) {
wrapper<Event>().enqueue(std::forward<Args>(args)...);
template<typename Event, typename... Params>
inline void enqueue(Params &&... params) {
wrapper<Event>().enqueue(std::forward<Params>(params)...);
}
/**
@@ -179,10 +184,11 @@ public:
* to reduce at a minimum the time spent in the bodies of the listeners.
*
* @tparam Event Type of events to send.
* @param args Arguments to forward along with the event.
*/
template<typename Event>
inline void update() {
wrapper<Event>().publish();
inline void update(Args... args) {
wrapper<Event>().publish(args...);
}
/**
@@ -191,13 +197,15 @@ public:
* 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.
*
* @param args Arguments to forward along with the event.
*/
inline void update() const {
inline void update(Args... args) const {
for(auto pos = wrappers.size(); pos; --pos) {
auto &wrapper = wrappers[pos-1];
if(wrapper) {
wrapper->publish();
wrapper->publish(args...);
}
}
}

View File

@@ -6,27 +6,27 @@ struct an_event {};
struct another_event {};
struct receiver {
void receive(const an_event &) { ++cnt; }
void receive(const an_event &, int value) { cnt += value; }
void reset() { cnt = 0; }
int cnt{0};
};
TEST(Dispatcher, Functionalities) {
entt::dispatcher dispatcher;
entt::dispatcher<int> dispatcher;
receiver receiver;
dispatcher.sink<an_event>().connect(&receiver);
dispatcher.trigger<an_event>();
dispatcher.trigger<an_event>(1);
dispatcher.enqueue<an_event>();
dispatcher.enqueue<another_event>();
dispatcher.update<another_event>();
dispatcher.update<another_event>(1);
ASSERT_EQ(receiver.cnt, 1);
dispatcher.update<an_event>();
dispatcher.trigger<an_event>();
dispatcher.update<an_event>(2);
dispatcher.trigger<an_event>(1);
ASSERT_EQ(receiver.cnt, 3);
ASSERT_EQ(receiver.cnt, 4);
receiver.reset();
@@ -34,10 +34,10 @@ TEST(Dispatcher, Functionalities) {
const an_event &cevent = event;
dispatcher.sink<an_event>().disconnect(&receiver);
dispatcher.trigger(an_event{});
dispatcher.trigger(an_event{}, 1);
dispatcher.enqueue(event);
dispatcher.update();
dispatcher.trigger(cevent);
dispatcher.update(1);
dispatcher.trigger(cevent, 1);
ASSERT_EQ(receiver.cnt, 0);
}