🛠️🐜 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.

597 lines
20 KiB

  1. # Crash Course: events, signals and everything in between
  2. <!--
  3. @cond TURN_OFF_DOXYGEN
  4. -->
  5. # Table of Contents
  6. * [Introduction](#introduction)
  7. * [Delegate](#delegate)
  8. * [Runtime arguments](#runtime-arguments)
  9. * [Lambda support](#lambda-support)
  10. * [Signals](#signals)
  11. * [Event dispatcher](#event-dispatcher)
  12. * [Named queues](#named-queues)
  13. * [Event emitter](#event-emitter)
  14. <!--
  15. @endcond TURN_OFF_DOXYGEN
  16. -->
  17. # Introduction
  18. Signals are usually a core part of games and software architectures in
  19. general.<br/>
  20. Roughly speaking, they help to decouple the various parts of a system while
  21. allowing them to communicate with each other somehow.
  22. The so called _modern C++_ comes with a tool that can be useful in these terms,
  23. the `std::function`. As an example, it can be used to create delegates.<br/>
  24. However, there is no guarantee that an `std::function` does not perform
  25. allocations under the hood and this could be problematic sometimes. Furthermore,
  26. it solves a problem but may not adapt well to other requirements that may arise
  27. from time to time.
  28. In case that the flexibility and power of an `std::function` isn't required or
  29. if the price to pay for them is too high,` EnTT` offers a complete set of
  30. lightweight classes to solve the same and many other problems.
  31. # Delegate
  32. A delegate can be used as a general purpose invoker with no memory overhead for
  33. free functions and members provided along with an instance on which to invoke
  34. them.<br/>
  35. It doesn't claim to be a drop-in replacement for an `std::function`, so don't
  36. expect to use it whenever an `std::function` fits well. That said, it's most
  37. likely even a better fit than an `std::function` in a lot of cases, so expect to
  38. use it quite a lot anyway.
  39. The interface is trivial. It offers a default constructor to create empty
  40. delegates:
  41. ```cpp
  42. entt::delegate<int(int)> delegate{};
  43. ```
  44. All what is needed to create an instance is to specify the type of the function
  45. the delegate will _contain_, that is the signature of the free function or the
  46. member one wants to assign to it.
  47. Attempting to use an empty delegate by invoking its function call operator
  48. results in undefined behavior or most likely a crash. Before to use a delegate,
  49. it must be initialized.<br/>
  50. There exists a bunch of overloads of the `connect` member function to do that.
  51. As an example of use:
  52. ```cpp
  53. int f(int i) { return i; }
  54. struct my_struct {
  55. int f(const int &i) const { return i; }
  56. };
  57. // bind a free function to the delegate
  58. delegate.connect<&f>();
  59. // bind a member function to the delegate
  60. my_struct instance;
  61. delegate.connect<&my_struct::f>(instance);
  62. ```
  63. The delegate class accepts also data members, if needed. In this case, the
  64. function type of the delegate is such that the parameter list is empty and the
  65. value of the data member is at least convertible to the return type.
  66. Free functions having type equivalent to `void(T &, args...)` are accepted as
  67. well. The first argument `T &` is considered a payload and the function will
  68. receive it back every time it's invoked. In other terms, this works just fine
  69. with the above definition:
  70. ```cpp
  71. void g(const char &c, int i) { /* ... */ }
  72. const char c = 'c';
  73. delegate.connect<&g>(c);
  74. delegate(42);
  75. ```
  76. The function `g` will be invoked with a reference to `c` and `42`. However, the
  77. function type of the delegate is still `void(int)`. This is also the signature
  78. of its function call operator.
  79. Another interesting aspect of the delegate class is that it accepts also
  80. functions with a list of parameters that is shorter than that of the function
  81. type used to specialize the delegate itself.<br/>
  82. The following code is therefore perfectly valid:
  83. ```cpp
  84. void g() { /* ... */ }
  85. delegate.connect<&g>();
  86. delegate(42);
  87. ```
  88. Where the function type of the delegate is `void(int)` as above. It goes without
  89. saying that the extra arguments are silently discarded internally.<br/>
  90. This is a nice-to-have feature in a lot of cases, as an example when the
  91. `delegate` class is used as a building block of a signal-slot system.
  92. To create and initialize a delegate at once, there are a few specialized
  93. constructors. Because of the rules of the language, the listener is provided by
  94. means of the `entt::connect_arg` variable template:
  95. ```cpp
  96. entt::delegate<int(int)> func{entt::connect_arg<&f>};
  97. ```
  98. Aside `connect`, a `disconnect` counterpart isn't provided. Instead, there
  99. exists a `reset` member function to use to clear a delegate.<br/>
  100. To know if a delegate is empty, it can be used explicitly in every conditional
  101. statement:
  102. ```cpp
  103. if(delegate) {
  104. // ...
  105. }
  106. ```
  107. Finally, to invoke a delegate, the function call operator is the way to go as
  108. already shown in the examples above:
  109. ```cpp
  110. auto ret = delegate(42);
  111. ```
  112. In all cases, the listeners don't have to strictly follow the signature of the
  113. delegate. As long as a listener can be invoked with the given arguments to yield
  114. a result that is convertible to the given result type, everything works just
  115. fine.
  116. As a side note, members of classes may or may not be associated with instances.
  117. If they are not, the first argument of the function type must be that of the
  118. class on which the members operate and an instance of this class must obviously
  119. be passed when invoking the delegate:
  120. ```cpp
  121. entt::delegate<void(my_struct &, int)> delegate;
  122. delegate.connect<&my_struct::f>();
  123. my_struct instance;
  124. delegate(instance, 42);
  125. ```
  126. In this case, it's not possible to deduce the function type since the first
  127. argument doesn't necessarily have to be a reference (for example, it can be a
  128. pointer, as well as a const reference).<br/>
  129. Therefore, the function type must be declared explicitly for unbound members.
  130. ## Runtime arguments
  131. The `delegate` class is meant to be used primarily with template arguments.
  132. However, as a consequence of its design, it can also offer minimal support for
  133. runtime arguments.<br/>
  134. When used in this modality, some feature aren't supported though. In particular:
  135. * Curried functions aren't accepted.
  136. * Functions with an argument list that differs from that of the delegate aren't
  137. supported.
  138. * Return type and types of arguments **must** coincide with those of the
  139. delegate and _being at least convertible_ isn't enough anymore.
  140. Moreover, for a given function type `Ret(Args...)`, the signature of the
  141. functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
  142. Runtime arguments can be passed both to the constructor of a delegate and to the
  143. `connect` member function. An optional parameter is also accepted in both cases.
  144. This argument is used to pass arbitrary user data back and forth as a
  145. `const void *` upon invocation.<br/>
  146. To connect a function to a delegate _in the hard way_:
  147. ```cpp
  148. int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
  149. const int value = 42;
  150. // use the constructor ...
  151. entt::delegate delegate{&func, &value};
  152. // ... or the connect member function
  153. delegate.connect(&func, &value);
  154. ```
  155. The type of the delegate is deduced from the function if possible. In this case,
  156. since the first argument is an implementation detail, the deduced function type
  157. is `int(int)`.<br/>
  158. Invoking a delegate built in this way follows the same rules as previously
  159. explained.
  160. ## Lambda support
  161. In general, the `delegate` class doesn't fully support lambda functions in all
  162. their nuances. The reason is pretty simple: a `delegate` isn't a drop-in
  163. replacement for an `std::function`. Instead, it tries to overcome the problems
  164. with the latter.<br/>
  165. That being said, non-capturing lambda functions are supported, even though some
  166. feature aren't available in this case.
  167. This is a logical consequence of the support for connecting functions at
  168. runtime. Therefore, lambda functions undergo the same rules and
  169. limitations.<br/>
  170. In fact, since non-capturing lambda functions decay to pointers to functions,
  171. they can be used with a `delegate` as if they were _normal functions_ with
  172. optional payload:
  173. ```cpp
  174. my_struct instance;
  175. // use the constructor ...
  176. entt::delegate delegate{+[](const void *ptr, int value) {
  177. return static_cast<const my_struct *>(ptr)->f(value);
  178. }, &instance};
  179. // ... or the connect member function
  180. delegate.connect([](const void *ptr, int value) {
  181. return static_cast<const my_struct *>(ptr)->f(value);
  182. }, &instance);
  183. ```
  184. As above, the first parameter (`const void *`) isn't part of the function type
  185. of the delegate and is used to dispatch arbitrary user data back and forth. In
  186. other terms, the function type of the delegate above is `int(int)`.
  187. # Signals
  188. Signal handlers work with references to classes, function pointers and pointers
  189. to members. Listeners can be any kind of objects and users are in charge of
  190. connecting and disconnecting them from a signal to avoid crashes due to
  191. different lifetimes. On the other side, performance shouldn't be affected that
  192. much by the presence of such a signal handler.<br/>
  193. Signals make use of delegates internally and therefore they undergo the same
  194. rules and offer similar functionalities. It may be a good idea to consult the
  195. documentation of the `delegate` class for further information.
  196. A signal handler can be used as a private data member without exposing any
  197. _publish_ functionality to the clients of a class. The basic idea is to impose a
  198. clear separation between the signal itself and the `sink` class, that is a tool
  199. to be used to connect and disconnect listeners on the fly.
  200. The API of a signal handler is straightforward. If a collector is supplied to
  201. the signal when something is published, all the values returned by the listeners
  202. can be literally _collected_ and used later by the caller. Otherwise, the class
  203. works just like a plain signal that emits events from time to time.<br/>
  204. To create instances of signal handlers it is sufficient to provide the type of
  205. function to which they refer:
  206. ```cpp
  207. entt::sigh<void(int, char)> signal;
  208. ```
  209. Signals offer all the basic functionalities required to know how many listeners
  210. they contain (`size`) or if they contain at least a listener (`empty`), as well
  211. as a function to use to swap handlers (`swap`).
  212. Besides them, there are member functions to use both to connect and disconnect
  213. listeners in all their forms by means of a sink:
  214. ```cpp
  215. void foo(int, char) { /* ... */ }
  216. struct listener {
  217. void bar(const int &, char) { /* ... */ }
  218. };
  219. // ...
  220. entt::sink sink{signal};
  221. listener instance;
  222. sink.connect<&foo>();
  223. sink.connect<&listener::bar>(instance);
  224. // ...
  225. // disconnects a free function
  226. sink.disconnect<&foo>();
  227. // disconnect a member function of an instance
  228. sink.disconnect<&listener::bar>(instance);
  229. // disconnect all member functions of an instance, if any
  230. sink.disconnect(instance);
  231. // discards all listeners at once
  232. sink.disconnect();
  233. ```
  234. As shown above, the listeners don't have to strictly follow the signature of the
  235. signal. As long as a listener can be invoked with the given arguments to yield a
  236. result that is convertible to the given return type, everything works just
  237. fine.<br/>
  238. It's also possible to connect a listener before other listeners already
  239. contained by the signal. The `before` function returns a `sink` object correctly
  240. initialized for the purpose that can be used to connect one or more listeners in
  241. order and in the desired position:
  242. ```cpp
  243. sink.before<&foo>().connect<&listener::bar>(instance);
  244. ```
  245. In all cases, the `connect` member function returns by default a `connection`
  246. object to be used as an alternative to break a connection by means of its
  247. `release` member function. A `scoped_connection` can also be created from a
  248. connection. In this case, the link is broken automatically as soon as the object
  249. goes out of scope.
  250. Once listeners are attached (or even if there are no listeners at all), events
  251. and data in general can be published through a signal by means of the `publish`
  252. member function:
  253. ```cpp
  254. signal.publish(42, 'c');
  255. ```
  256. To collect data, the `collect` member function should be used instead. Below is
  257. a minimal example to show how to use it:
  258. ```cpp
  259. int f() { return 0; }
  260. int g() { return 1; }
  261. // ...
  262. entt::sigh<int()> signal;
  263. entt::sink sink{signal};
  264. sink.connect<&f>();
  265. sink.connect<&g>();
  266. std::vector<int> vec{};
  267. signal.collect([&vec](int value) { vec.push_back(value); });
  268. assert(vec[0] == 0);
  269. assert(vec[1] == 1);
  270. ```
  271. A collector must expose a function operator that accepts as an argument a type
  272. to which the return type of the listeners can be converted. Moreover, it can
  273. optionally return a boolean value that is true to stop collecting data, false
  274. otherwise. This way one can avoid calling all the listeners in case it isn't
  275. necessary.<br/>
  276. Functors can also be used in place of a lambda. Since the collector is copied
  277. when invoking the `collect` member function, `std::ref` is the way to go in this
  278. case:
  279. ```cpp
  280. struct my_collector {
  281. std::vector<int> vec{};
  282. bool operator()(int v) {
  283. vec.push_back(v);
  284. return true;
  285. }
  286. };
  287. // ...
  288. my_collector collector;
  289. signal.collect(std::ref(collector));
  290. ```
  291. # Event dispatcher
  292. The event dispatcher class allows users to trigger immediate events or to queue
  293. and publish them all together later.<br/>
  294. This class lazily instantiates its queues. Therefore, it's not necessary to
  295. _announce_ the event types in advance:
  296. ```cpp
  297. // define a general purpose dispatcher
  298. entt::dispatcher dispatcher{};
  299. ```
  300. A listener registered with a dispatcher is such that its type offers one or more
  301. member functions that take arguments of type `Event &` for any type of event,
  302. regardless of the return value.<br/>
  303. These functions are linked directly via `connect` to a _sink_:
  304. ```cpp
  305. struct an_event { int value; };
  306. struct another_event {};
  307. struct listener {
  308. void receive(const an_event &) { /* ... */ }
  309. void method(const another_event &) { /* ... */ }
  310. };
  311. // ...
  312. listener listener;
  313. dispatcher.sink<an_event>().connect<&listener::receive>(listener);
  314. dispatcher.sink<another_event>().connect<&listener::method>(listener);
  315. ```
  316. The `disconnect` member function is used to remove one listener at a time or all
  317. of them at once:
  318. ```cpp
  319. dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
  320. dispatcher.sink<another_event>().disconnect(listener);
  321. ```
  322. The `trigger` member function serves the purpose of sending an immediate event
  323. to all the listeners registered so far:
  324. ```cpp
  325. dispatcher.trigger(an_event{42});
  326. dispatcher.trigger<another_event>();
  327. ```
  328. Listeners are invoked immediately, order of execution isn't guaranteed. This
  329. method can be used to push around urgent messages like an _is terminating_
  330. notification on a mobile app.
  331. On the other hand, the `enqueue` member function queues messages together and
  332. helps to maintain control over the moment they are sent to listeners:
  333. ```cpp
  334. dispatcher.enqueue<an_event>(42);
  335. dispatcher.enqueue(another_event{});
  336. ```
  337. Events are stored aside until the `update` member function is invoked:
  338. ```cpp
  339. // emits all the events of the given type at once
  340. dispatcher.update<an_event>();
  341. // emits all the events queued so far at once
  342. dispatcher.update();
  343. ```
  344. This way users can embed the dispatcher in a loop and literally dispatch events
  345. once per tick to their systems.
  346. ## Named queues
  347. All queues within a dispatcher are associated by default with an event type and
  348. then retrieved from it.<br/>
  349. However, it's possible to create queues with different _names_ (and therefore
  350. also multiple queues for a single type). In fact, more or less all functions
  351. also take an additional parameter. As an example:
  352. ```cpp
  353. dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
  354. ```
  355. In this case, the term _name_ is misused as these are actual numeric identifiers
  356. of type `id_type`.<br/>
  357. An exception to this rule is the `enqueue` function. There is no additional
  358. parameter for it but rather a different function:
  359. ```cpp
  360. dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
  361. ```
  362. This is mainly due to the template argument deduction rules and unfortunately
  363. there is no real (elegant) way to avoid it.
  364. # Event emitter
  365. A general purpose event emitter thought mainly for those cases where it comes to
  366. working with asynchronous stuff.<br/>
  367. Originally designed to fit the requirements of
  368. [`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
  369. modern C++), it was adapted later to be included in this library.
  370. To create a custom emitter type, derived classes must inherit directly from the
  371. base class as:
  372. ```cpp
  373. struct my_emitter: emitter<my_emitter> {
  374. // ...
  375. }
  376. ```
  377. The full list of accepted types of events isn't required. Handlers are created
  378. internally on the fly and thus each type of event is accepted by default.
  379. Whenever an event is published, an emitter provides the listeners with a
  380. reference to itself along with a reference to the event. Therefore listeners
  381. have an handy way to work with it without incurring in the need of capturing a
  382. reference to the emitter itself.<br/>
  383. In addition, an opaque object is returned each time a connection is established
  384. between an emitter and a listener, allowing the caller to disconnect them at a
  385. later time.<br/>
  386. The opaque object used to handle connections is both movable and copyable. On
  387. the other side, an event emitter is movable but not copyable by default.
  388. To create new instances of an emitter, no arguments are required:
  389. ```cpp
  390. my_emitter emitter{};
  391. ```
  392. Listeners must be movable and callable objects (free functions, lambdas,
  393. functors, `std::function`s, whatever) whose function type is compatible with:
  394. ```cpp
  395. void(Event &, my_emitter &)
  396. ```
  397. Where `Event` is the type of event they want to listen.<br/>
  398. There are two ways to attach a listener to an event emitter that differ
  399. slightly from each other:
  400. * To register a long-lived listener, use the `on` member function. It is meant
  401. to register a listener designed to be invoked more than once for the given
  402. event type.<br/>
  403. As an example:
  404. ```cpp
  405. auto conn = emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
  406. // ...
  407. });
  408. ```
  409. The connection object can be freely discarded. Otherwise, it can be used later
  410. to disconnect the listener if required.
  411. * To register a short-lived listener, use the `once` member function. It is
  412. meant to register a listener designed to be invoked only once for the given
  413. event type. The listener is automatically disconnected after the first
  414. invocation.<br/>
  415. As an example:
  416. ```cpp
  417. auto conn = emitter.once<my_event>([](const my_event &event, my_emitter &emitter) {
  418. // ...
  419. });
  420. ```
  421. The connection object can be freely discarded. Otherwise, it can be used later
  422. to disconnect the listener if required.
  423. In both cases, the connection object can be used with the `erase` member
  424. function:
  425. ```cpp
  426. emitter.erase(conn);
  427. ```
  428. There are also two member functions to use either to disconnect all the
  429. listeners for a given type of event or to clear the emitter:
  430. ```cpp
  431. // removes all the listener for the specific event
  432. emitter.clear<my_event>();
  433. // removes all the listeners registered so far
  434. emitter.clear();
  435. ```
  436. To send an event to all the listeners that are interested in it, the `publish`
  437. member function offers a convenient approach that relieves users from having to
  438. create the event:
  439. ```cpp
  440. struct my_event { int i; };
  441. // ...
  442. emitter.publish<my_event>(42);
  443. ```
  444. Finally, the `empty` member function tests if there exists at least either a
  445. listener registered with the event emitter or to a given type of event:
  446. ```cpp
  447. bool empty;
  448. // checks if there is any listener registered for the specific event
  449. empty = emitter.empty<my_event>();
  450. // checks it there are listeners registered with the event emitter
  451. empty = emitter.empty();
  452. ```
  453. In general, the event emitter is a handy tool when the derived classes _wrap_
  454. asynchronous operations, because it introduces a _nice-to-have_ model based on
  455. events and listeners that kindly hides the complexity behind the scenes. However
  456. it is not limited to such uses.