EnTT 3.14.0
Loading...
Searching...
No Matches
dispatcher.hpp
1#ifndef ENTT_SIGNAL_DISPATCHER_HPP
2#define ENTT_SIGNAL_DISPATCHER_HPP
3
4#include <cstddef>
5#include <functional>
6#include <memory>
7#include <type_traits>
8#include <utility>
9#include <vector>
10#include "../container/dense_map.hpp"
11#include "../core/compressed_pair.hpp"
12#include "../core/fwd.hpp"
13#include "../core/type_info.hpp"
14#include "../core/utility.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 std::size_t size() const noexcept = 0;
29};
30
31template<typename Type, typename Allocator>
32class dispatcher_handler final: public basic_dispatcher_handler {
33 static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type");
34
35 using alloc_traits = std::allocator_traits<Allocator>;
36 using signal_type = sigh<void(Type &), Allocator>;
37 using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
38
39public:
40 using allocator_type = Allocator;
41
42 dispatcher_handler(const allocator_type &allocator)
43 : signal{allocator},
44 events{allocator} {}
45
46 void publish() override {
47 const auto length = events.size();
48
49 for(std::size_t pos{}; pos < length; ++pos) {
50 signal.publish(events[pos]);
51 }
52
53 events.erase(events.cbegin(), events.cbegin() + length);
54 }
55
56 void disconnect(void *instance) override {
57 bucket().disconnect(instance);
58 }
59
60 void clear() noexcept override {
61 events.clear();
62 }
63
64 [[nodiscard]] auto bucket() noexcept {
65 return typename signal_type::sink_type{signal};
66 }
67
68 void trigger(Type event) {
69 signal.publish(event);
70 }
71
72 template<typename... Args>
73 void enqueue(Args &&...args) {
74 if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
75 events.push_back(Type{std::forward<Args>(args)...});
76 } else {
77 events.emplace_back(std::forward<Args>(args)...);
78 }
79 }
80
81 [[nodiscard]] std::size_t size() const noexcept override {
82 return events.size();
83 }
84
85private:
86 signal_type signal;
87 container_type events;
88};
89
90} // namespace internal
107template<typename Allocator>
109 template<typename Type>
110 using handler_type = internal::dispatcher_handler<Type, Allocator>;
111
112 using key_type = id_type;
113 // std::shared_ptr because of its type erased allocator which is useful here
114 using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
115
116 using alloc_traits = std::allocator_traits<Allocator>;
117 using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
119
120 template<typename Type>
121 [[nodiscard]] handler_type<Type> &assure(const id_type id) {
122 static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
123 auto &&ptr = pools.first()[id];
124
125 if(!ptr) {
126 const auto &allocator = get_allocator();
127 ptr = std::allocate_shared<handler_type<Type>>(allocator, allocator);
128 }
129
130 return static_cast<handler_type<Type> &>(*ptr);
131 }
132
133 template<typename Type>
134 [[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
135 static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
136
137 if(auto it = pools.first().find(id); it != pools.first().cend()) {
138 return static_cast<const handler_type<Type> *>(it->second.get());
139 }
140
141 return nullptr;
142 }
143
144public:
146 using allocator_type = Allocator;
148 using size_type = std::size_t;
149
153
158 explicit basic_dispatcher(const allocator_type &allocator)
159 : pools{allocator, allocator} {}
160
163
169 : pools{std::move(other.pools)} {}
170
177 : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
178 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
179 }
180
182 ~basic_dispatcher() = default;
183
189
196 ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
197 swap(other);
198 return *this;
199 }
200
205 void swap(basic_dispatcher &other) noexcept {
206 using std::swap;
207 swap(pools, other.pools);
208 }
209
214 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
215 return pools.second();
216 }
217
224 template<typename Type>
225 [[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
226 const auto *cpool = assure<std::decay_t<Type>>(id);
227 return cpool ? cpool->size() : 0u;
228 }
229
234 [[nodiscard]] size_type size() const noexcept {
235 size_type count{};
236
237 for(auto &&cpool: pools.first()) {
238 count += cpool.second->size();
239 }
240
241 return count;
242 }
243
263 template<typename Type>
264 [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
265 return assure<Type>(id).bucket();
266 }
267
273 template<typename Type>
274 void trigger(Type &&value = {}) {
275 trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
276 }
277
284 template<typename Type>
285 void trigger(const id_type id, Type &&value = {}) {
286 assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value));
287 }
288
295 template<typename Type, typename... Args>
296 void enqueue(Args &&...args) {
297 enqueue_hint<Type>(type_hash<Type>::value(), std::forward<Args>(args)...);
298 }
299
305 template<typename Type>
306 void enqueue(Type &&value) {
307 enqueue_hint(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
308 }
309
317 template<typename Type, typename... Args>
318 void enqueue_hint(const id_type id, Args &&...args) {
319 assure<Type>(id).enqueue(std::forward<Args>(args)...);
320 }
321
328 template<typename Type>
329 void enqueue_hint(const id_type id, Type &&value) {
330 assure<std::decay_t<Type>>(id).enqueue(std::forward<Type>(value));
331 }
332
339 template<typename Type>
340 void disconnect(Type &value_or_instance) {
341 disconnect(&value_or_instance);
342 }
343
350 template<typename Type>
351 void disconnect(Type *value_or_instance) {
352 for(auto &&cpool: pools.first()) {
353 cpool.second->disconnect(value_or_instance);
354 }
355 }
356
362 template<typename Type>
364 assure<Type>(id).clear();
365 }
366
368 void clear() noexcept {
369 for(auto &&cpool: pools.first()) {
370 cpool.second->clear();
371 }
372 }
373
379 template<typename Type>
381 assure<Type>(id).publish();
382 }
383
385 void update() const {
386 for(auto &&cpool: pools.first()) {
387 cpool.second->publish();
388 }
389 }
390
391private:
393};
394
395} // namespace entt
396
397#endif
Basic dispatcher implementation.
void clear(const id_type id=type_hash< Type >::value())
Discards all the events stored so far in a given queue.
void trigger(Type &&value={})
Triggers an immediate event of a given type.
void swap(basic_dispatcher &other) noexcept
Exchanges the contents with those of a given dispatcher.
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.
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)
Enqueues an event of the given type.
size_type size() const noexcept
Returns the total number of pending events.
std::size_t size_type
Unsigned integer 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.
void trigger(const id_type id, Type &&value={})
Triggers an immediate event on a queue of a given type.
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.
EnTT default namespace.
Definition dense_map.hpp:22
std::uint32_t id_type
Alias declaration for type identifiers.
Definition fwd.hpp:14
constexpr void swap(compressed_pair< First, Second > &lhs, compressed_pair< First, Second > &rhs)
Swaps two compressed pair objects.
Type hash.
Definition type_info.hpp:92