dispatcher.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. #ifndef ENTT_SIGNAL_DISPATCHER_HPP
  2. #define ENTT_SIGNAL_DISPATCHER_HPP
  3. #include <cstddef>
  4. #include <functional>
  5. #include <memory>
  6. #include <type_traits>
  7. #include <utility>
  8. #include <vector>
  9. #include "../container/dense_map.hpp"
  10. #include "../core/compressed_pair.hpp"
  11. #include "../core/fwd.hpp"
  12. #include "../core/type_info.hpp"
  13. #include "../core/utility.hpp"
  14. #include "fwd.hpp"
  15. #include "sigh.hpp"
  16. namespace entt {
  17. /**
  18. * @cond TURN_OFF_DOXYGEN
  19. * Internal details not to be documented.
  20. */
  21. namespace internal {
  22. struct basic_dispatcher_handler {
  23. virtual ~basic_dispatcher_handler() = default;
  24. virtual void publish() = 0;
  25. virtual void disconnect(void *) = 0;
  26. virtual void clear() noexcept = 0;
  27. virtual std::size_t size() const noexcept = 0;
  28. };
  29. template<typename Type, typename Allocator>
  30. class dispatcher_handler final: public basic_dispatcher_handler {
  31. static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Invalid type");
  32. using alloc_traits = std::allocator_traits<Allocator>;
  33. using signal_type = sigh<void(Type &), Allocator>;
  34. using container_type = std::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
  35. public:
  36. using allocator_type = Allocator;
  37. dispatcher_handler(const allocator_type &allocator)
  38. : signal{allocator},
  39. events{allocator} {}
  40. void publish() override {
  41. const auto length = events.size();
  42. for(std::size_t pos{}; pos < length; ++pos) {
  43. signal.publish(events[pos]);
  44. }
  45. events.erase(events.cbegin(), events.cbegin() + length);
  46. }
  47. void disconnect(void *instance) override {
  48. bucket().disconnect(instance);
  49. }
  50. void clear() noexcept override {
  51. events.clear();
  52. }
  53. [[nodiscard]] auto bucket() noexcept {
  54. return typename signal_type::sink_type{signal};
  55. }
  56. void trigger(Type event) {
  57. signal.publish(event);
  58. }
  59. template<typename... Args>
  60. void enqueue(Args &&...args) {
  61. if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) {
  62. events.push_back(Type{std::forward<Args>(args)...});
  63. } else {
  64. events.emplace_back(std::forward<Args>(args)...);
  65. }
  66. }
  67. std::size_t size() const noexcept override {
  68. return events.size();
  69. }
  70. private:
  71. signal_type signal;
  72. container_type events;
  73. };
  74. } // namespace internal
  75. /**
  76. * Internal details not to be documented.
  77. * @endcond
  78. */
  79. /**
  80. * @brief Basic dispatcher implementation.
  81. *
  82. * A dispatcher can be used either to trigger an immediate event or to enqueue
  83. * events to be published all together once per tick.<br/>
  84. * Listeners are provided in the form of member functions. For each event of
  85. * type `Type`, listeners are such that they can be invoked with an argument of
  86. * type `Type &`, no matter what the return type is.
  87. *
  88. * The dispatcher creates instances of the `sigh` class internally. Refer to the
  89. * documentation of the latter for more details.
  90. *
  91. * @tparam Allocator Type of allocator used to manage memory and elements.
  92. */
  93. template<typename Allocator>
  94. class basic_dispatcher {
  95. template<typename Type>
  96. using handler_type = internal::dispatcher_handler<Type, Allocator>;
  97. using key_type = id_type;
  98. // std::shared_ptr because of its type erased allocator which is useful here
  99. using mapped_type = std::shared_ptr<internal::basic_dispatcher_handler>;
  100. using alloc_traits = std::allocator_traits<Allocator>;
  101. using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const key_type, mapped_type>>;
  102. using container_type = dense_map<key_type, mapped_type, identity, std::equal_to<key_type>, container_allocator>;
  103. template<typename Type>
  104. [[nodiscard]] handler_type<Type> &assure(const id_type id) {
  105. static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
  106. auto &&ptr = pools.first()[id];
  107. if(!ptr) {
  108. const auto &allocator = get_allocator();
  109. ptr = std::allocate_shared<handler_type<Type>>(allocator, allocator);
  110. }
  111. return static_cast<handler_type<Type> &>(*ptr);
  112. }
  113. template<typename Type>
  114. [[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
  115. static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
  116. if(auto it = pools.first().find(id); it != pools.first().cend()) {
  117. return static_cast<const handler_type<Type> *>(it->second.get());
  118. }
  119. return nullptr;
  120. }
  121. public:
  122. /*! @brief Allocator type. */
  123. using allocator_type = Allocator;
  124. /*! @brief Unsigned integer type. */
  125. using size_type = std::size_t;
  126. /*! @brief Default constructor. */
  127. basic_dispatcher()
  128. : basic_dispatcher{allocator_type{}} {}
  129. /**
  130. * @brief Constructs a dispatcher with a given allocator.
  131. * @param allocator The allocator to use.
  132. */
  133. explicit basic_dispatcher(const allocator_type &allocator)
  134. : pools{allocator, allocator} {}
  135. /**
  136. * @brief Move constructor.
  137. * @param other The instance to move from.
  138. */
  139. basic_dispatcher(basic_dispatcher &&other) noexcept
  140. : pools{std::move(other.pools)} {}
  141. /**
  142. * @brief Allocator-extended move constructor.
  143. * @param other The instance to move from.
  144. * @param allocator The allocator to use.
  145. */
  146. basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept
  147. : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {
  148. ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
  149. }
  150. /**
  151. * @brief Move assignment operator.
  152. * @param other The instance to move from.
  153. * @return This dispatcher.
  154. */
  155. basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
  156. ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
  157. pools = std::move(other.pools);
  158. return *this;
  159. }
  160. /**
  161. * @brief Exchanges the contents with those of a given dispatcher.
  162. * @param other Dispatcher to exchange the content with.
  163. */
  164. void swap(basic_dispatcher &other) {
  165. using std::swap;
  166. swap(pools, other.pools);
  167. }
  168. /**
  169. * @brief Returns the associated allocator.
  170. * @return The associated allocator.
  171. */
  172. [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
  173. return pools.second();
  174. }
  175. /**
  176. * @brief Returns the number of pending events for a given type.
  177. * @tparam Type Type of event for which to return the count.
  178. * @param id Name used to map the event queue within the dispatcher.
  179. * @return The number of pending events for the given type.
  180. */
  181. template<typename Type>
  182. size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
  183. const auto *cpool = assure<std::decay_t<Type>>(id);
  184. return cpool ? cpool->size() : 0u;
  185. }
  186. /**
  187. * @brief Returns the total number of pending events.
  188. * @return The total number of pending events.
  189. */
  190. size_type size() const noexcept {
  191. size_type count{};
  192. for(auto &&cpool: pools.first()) {
  193. count += cpool.second->size();
  194. }
  195. return count;
  196. }
  197. /**
  198. * @brief Returns a sink object for the given event and queue.
  199. *
  200. * A sink is an opaque object used to connect listeners to events.
  201. *
  202. * The function type for a listener is _compatible_ with:
  203. *
  204. * @code{.cpp}
  205. * void(Type &);
  206. * @endcode
  207. *
  208. * The order of invocation of the listeners isn't guaranteed.
  209. *
  210. * @sa sink
  211. *
  212. * @tparam Type Type of event of which to get the sink.
  213. * @param id Name used to map the event queue within the dispatcher.
  214. * @return A temporary sink object.
  215. */
  216. template<typename Type>
  217. [[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
  218. return assure<Type>(id).bucket();
  219. }
  220. /**
  221. * @brief Triggers an immediate event of a given type.
  222. * @tparam Type Type of event to trigger.
  223. * @param value An instance of the given type of event.
  224. */
  225. template<typename Type>
  226. void trigger(Type &&value = {}) {
  227. trigger(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
  228. }
  229. /**
  230. * @brief Triggers an immediate event on a queue of a given type.
  231. * @tparam Type Type of event to trigger.
  232. * @param value An instance of the given type of event.
  233. * @param id Name used to map the event queue within the dispatcher.
  234. */
  235. template<typename Type>
  236. void trigger(const id_type id, Type &&value = {}) {
  237. assure<std::decay_t<Type>>(id).trigger(std::forward<Type>(value));
  238. }
  239. /**
  240. * @brief Enqueues an event of the given type.
  241. * @tparam Type Type of event to enqueue.
  242. * @tparam Args Types of arguments to use to construct the event.
  243. * @param args Arguments to use to construct the event.
  244. */
  245. template<typename Type, typename... Args>
  246. void enqueue(Args &&...args) {
  247. enqueue_hint<Type>(type_hash<Type>::value(), std::forward<Args>(args)...);
  248. }
  249. /**
  250. * @brief Enqueues an event of the given type.
  251. * @tparam Type Type of event to enqueue.
  252. * @param value An instance of the given type of event.
  253. */
  254. template<typename Type>
  255. void enqueue(Type &&value) {
  256. enqueue_hint(type_hash<std::decay_t<Type>>::value(), std::forward<Type>(value));
  257. }
  258. /**
  259. * @brief Enqueues an event of the given type.
  260. * @tparam Type Type of event to enqueue.
  261. * @tparam Args Types of arguments to use to construct the event.
  262. * @param id Name used to map the event queue within the dispatcher.
  263. * @param args Arguments to use to construct the event.
  264. */
  265. template<typename Type, typename... Args>
  266. void enqueue_hint(const id_type id, Args &&...args) {
  267. assure<Type>(id).enqueue(std::forward<Args>(args)...);
  268. }
  269. /**
  270. * @brief Enqueues an event of the given type.
  271. * @tparam Type Type of event to enqueue.
  272. * @param id Name used to map the event queue within the dispatcher.
  273. * @param value An instance of the given type of event.
  274. */
  275. template<typename Type>
  276. void enqueue_hint(const id_type id, Type &&value) {
  277. assure<std::decay_t<Type>>(id).enqueue(std::forward<Type>(value));
  278. }
  279. /**
  280. * @brief Utility function to disconnect everything related to a given value
  281. * or instance from a dispatcher.
  282. * @tparam Type Type of class or type of payload.
  283. * @param value_or_instance A valid object that fits the purpose.
  284. */
  285. template<typename Type>
  286. void disconnect(Type &value_or_instance) {
  287. disconnect(&value_or_instance);
  288. }
  289. /**
  290. * @brief Utility function to disconnect everything related to a given value
  291. * or instance from a dispatcher.
  292. * @tparam Type Type of class or type of payload.
  293. * @param value_or_instance A valid object that fits the purpose.
  294. */
  295. template<typename Type>
  296. void disconnect(Type *value_or_instance) {
  297. for(auto &&cpool: pools.first()) {
  298. cpool.second->disconnect(value_or_instance);
  299. }
  300. }
  301. /**
  302. * @brief Discards all the events stored so far in a given queue.
  303. * @tparam Type Type of event to discard.
  304. * @param id Name used to map the event queue within the dispatcher.
  305. */
  306. template<typename Type>
  307. void clear(const id_type id = type_hash<Type>::value()) {
  308. assure<Type>(id).clear();
  309. }
  310. /*! @brief Discards all the events queued so far. */
  311. void clear() noexcept {
  312. for(auto &&cpool: pools.first()) {
  313. cpool.second->clear();
  314. }
  315. }
  316. /**
  317. * @brief Delivers all the pending events of a given queue.
  318. * @tparam Type Type of event to send.
  319. * @param id Name used to map the event queue within the dispatcher.
  320. */
  321. template<typename Type>
  322. void update(const id_type id = type_hash<Type>::value()) {
  323. assure<Type>(id).publish();
  324. }
  325. /*! @brief Delivers all the pending events. */
  326. void update() const {
  327. for(auto &&cpool: pools.first()) {
  328. cpool.second->publish();
  329. }
  330. }
  331. private:
  332. compressed_pair<container_type, allocator_type> pools;
  333. };
  334. } // namespace entt
  335. #endif