EnTT 4.0.0
Loading...
Searching...
No Matches
dispatcher.hpp
1#ifndef ENTT_SIGNAL_DISPATCHER_HPP
2#define ENTT_SIGNAL_DISPATCHER_HPP
3
4#include "../container/dense_map.hpp"
5#include "../core/compressed_pair.hpp"
6#include "../core/concepts.hpp"
7#include "../core/fwd.hpp"
8#include "../core/type_info.hpp"
9#include "../stl/cstddef.hpp"
10#include "../stl/functional.hpp"
11#include "../stl/memory.hpp"
12#include "../stl/type_traits.hpp"
13#include "../stl/utility.hpp"
14#include "../stl/vector.hpp"
15#include "fwd.hpp"
16#include "sigh.hpp"
17
18namespace entt {
19
21namespace internal {
22
23struct basic_dispatcher_handler {
24 virtual ~basic_dispatcher_handler() = default;
25 virtual void publish() = 0;
26 virtual void disconnect(void *) = 0;
27 virtual void clear() noexcept = 0;
28 [[nodiscard]] virtual stl::size_t size() const noexcept = 0;
29};
30
31template<cvref_unqualified Type, typename Allocator>
32class dispatcher_handler final: public basic_dispatcher_handler {
33 using alloc_traits = stl::allocator_traits<Allocator>;
34 using signal_type = sigh<void(Type &), Allocator>;
35 using container_type = stl::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
36
37public:
38 using allocator_type = Allocator;
39
40 dispatcher_handler(const allocator_type &allocator)
41 : signal{allocator},
42 events{allocator} {}
43
44 void publish() override {
45 container_type other{};
46 other.swap(events);
47
48 for(auto &&elem: other) {
49 signal.publish(elem);
50 }
51 }
52
53 void disconnect(void *instance) override {
54 bucket().disconnect(instance);
55 }
56
57 void clear() noexcept override {
58 events.clear();
59 }
60
61 [[nodiscard]] auto bucket() noexcept {
62 return typename signal_type::sink_type{signal};
63 }
64
65 void trigger(Type &event) {
66 signal.publish(event);
67 }
68
69 template<typename... Args>
70 void enqueue(Args &&...args) {
71 if constexpr(stl::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<Type>)) {
72 events.push_back(Type{stl::forward<Args>(args)...});
73 } else {
74 events.emplace_back(stl::forward<Args>(args)...);
75 }
76 }
77
78 [[nodiscard]] stl::size_t size() const noexcept override {
79 return events.size();
80 }
81
82private:
83 signal_type signal;
84 container_type events;
85};
86
87} // namespace internal
89
104template<typename Allocator>
106 template<typename Type>
107 using handler_type = internal::dispatcher_handler<Type, Allocator>;
108
109 using key_type = id_type;
110 // stl::shared_ptr because of its type erased allocator which is useful here
111 using mapped_type = stl::shared_ptr<internal::basic_dispatcher_handler>;
112
113 using alloc_traits = stl::allocator_traits<Allocator>;
114 using container_allocator = alloc_traits::template rebind_alloc<stl::pair<const key_type, mapped_type>>;
115 using container_type = dense_map<key_type, mapped_type, stl::identity, stl::equal_to<>, container_allocator>;
116
117 template<cvref_unqualified Type>
118 [[nodiscard]] handler_type<Type> &assure(const id_type id) {
119 auto &&ptr = pools.first()[id];
120
121 if(!ptr) {
122 const auto &allocator = get_allocator();
123 ptr = stl::allocate_shared<handler_type<Type>>(allocator, allocator);
124 }
125
126 return static_cast<handler_type<Type> &>(*ptr);
127 }
128
129 template<cvref_unqualified Type>
130 [[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
131 if(auto it = pools.first().find(id); it != pools.first().cend()) {
132 return static_cast<const handler_type<Type> *>(it->second.get());
133 }
134
135 return nullptr;
136 }
137
138public:
140 using allocator_type = Allocator;
142 using size_type = stl::size_t;
143
147
152 explicit basic_dispatcher(const allocator_type &allocator)
153 : pools{allocator, allocator} {}
154
157
163 : pools{stl::move(other.pools)} {}
164
171 : pools{container_type{stl::move(other.pools.first()), allocator}, allocator} {
172 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
173 }
174
176 ~basic_dispatcher() = default;
177
183
190 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
191 swap(other);
192 return *this;
193 }
194
199 void swap(basic_dispatcher &other) noexcept {
200 using stl::swap;
201 swap(pools, other.pools);
202 }
203
208 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
209 return pools.second();
210 }
211
218 template<typename Type>
219 [[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
220 const auto *cpool = assure<stl::decay_t<Type>>(id);
221 return cpool ? cpool->size() : 0u;
222 }
223
228 [[nodiscard]] size_type size() const noexcept {
229 size_type count{};
230
231 for(auto &&cpool: pools.first()) {
232 count += cpool.second->size();
233 }
234
235 return count;
236 }
237
257 template<typename Type>
258 [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
259 return assure<Type>(id).bucket();
260 }
261
267 template<typename Type>
268 void trigger(Type value) {
269 trigger(type_hash<stl::decay_t<Type>>::value(), value);
270 }
271
278 template<typename Type>
279 void trigger(const id_type id, Type value) {
280 assure<stl::decay_t<Type>>(id).trigger(value);
281 }
282
289 template<typename Type, typename... Args>
290 void enqueue(Args &&...args) {
291 enqueue_hint<Type>(type_hash<Type>::value(), stl::forward<Args>(args)...);
292 }
293
299 template<typename Type>
300 void enqueue(Type &&value) {
301 enqueue_hint(type_hash<stl::decay_t<Type>>::value(), stl::forward<Type>(value));
302 }
303
311 template<typename Type, typename... Args>
312 void enqueue_hint(const id_type id, Args &&...args) {
313 assure<Type>(id).enqueue(stl::forward<Args>(args)...);
314 }
315
322 template<typename Type>
323 void enqueue_hint(const id_type id, Type &&value) {
324 assure<stl::decay_t<Type>>(id).enqueue(stl::forward<Type>(value));
325 }
326
333 template<typename Type>
334 void disconnect(Type &value_or_instance) {
335 disconnect(&value_or_instance);
336 }
337
344 template<typename Type>
345 void disconnect(Type *value_or_instance) {
346 for(auto &&cpool: pools.first()) {
347 cpool.second->disconnect(value_or_instance);
348 }
349 }
350
356 template<typename Type>
358 assure<Type>(id).clear();
359 }
360
362 void clear() noexcept {
363 for(auto &&cpool: pools.first()) {
364 cpool.second->clear();
365 }
366 }
367
373 template<typename Type>
375 assure<Type>(id).publish();
376 }
377
379 void update() const {
380 for(auto &&cpool: pools.first()) {
381 cpool.second->publish();
382 }
383 }
384
385private:
387};
388
389} // namespace entt
390
391#endif
void clear(const id_type id=type_hash< Type >::value())
Discards all the events stored so far in a given queue.
stl::size_t size_type
Unsigned integer type.
void swap(basic_dispatcher &other) noexcept
void clear() noexcept
Discards all the events queued so far.
void enqueue_hint(const id_type id, Type &&value)
Enqueues an event of the given type.
void trigger(Type value)
Triggers an immediate event of a given type.
Allocator allocator_type
Allocator type.
auto sink(const id_type id=type_hash< Type >::value())
Returns a sink object for the given event and queue.
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator)
Allocator-extended move constructor.
void update() const
Delivers all the pending events.
void enqueue(Type &&value)
Enqueues an event of the given type.
void enqueue_hint(const id_type id, Args &&...args)
size_type size() const noexcept
Returns the total number of pending events.
void trigger(const id_type id, Type value)
Triggers an immediate event on a queue of a given type.
void disconnect(Type &value_or_instance)
Utility function to disconnect everything related to a given value or instance from a dispatcher.
void update(const id_type id=type_hash< Type >::value())
Delivers all the pending events of a given queue.
size_type size(const id_type id=type_hash< Type >::value()) const noexcept
Returns the number of pending events for a given type.
basic_dispatcher(basic_dispatcher &&other) noexcept
Move constructor.
basic_dispatcher & operator=(const basic_dispatcher &)=delete
Default copy assignment operator, deleted on purpose.
~basic_dispatcher()=default
Default destructor.
basic_dispatcher(const basic_dispatcher &)=delete
Default copy constructor, deleted on purpose.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
basic_dispatcher()
Default constructor.
basic_dispatcher(const allocator_type &allocator)
Constructs a dispatcher with a given allocator.
void disconnect(Type *value_or_instance)
Utility function to disconnect everything related to a given value or instance from a dispatcher.
basic_dispatcher & operator=(basic_dispatcher &&other) noexcept
Move assignment operator.
void enqueue(Args &&...args)
Enqueues an event of the given type.
A compressed pair.
Associative container for key-value pairs with unique keys.
Custom EnTT namespace for the standard template library.
Definition entt.hpp:5
EnTT default namespace.
Definition dense_map.hpp:25
stl::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:29
static constexpr id_type value() noexcept
Returns the numeric representation of a given type.