356 lines
37 KiB
HTML
356 lines
37 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
|
<meta name="generator" content="Doxygen 1.9.1"/>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
<title>EnTT: Crash Course: events, signals and everything in between</title>
|
|
<link href="tabs.css" rel="stylesheet" type="text/css"/>
|
|
<script type="text/javascript" src="jquery.js"></script>
|
|
<script type="text/javascript" src="dynsections.js"></script>
|
|
<link href="search/search.css" rel="stylesheet" type="text/css"/>
|
|
<script type="text/javascript" src="search/searchdata.js"></script>
|
|
<script type="text/javascript" src="search/search.js"></script>
|
|
<link href="doxygen.css" rel="stylesheet" type="text/css" />
|
|
</head>
|
|
<body>
|
|
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
|
<div id="titlearea">
|
|
<table cellspacing="0" cellpadding="0">
|
|
<tbody>
|
|
<tr style="height: 56px;">
|
|
<td id="projectalign" style="padding-left: 0.5em;">
|
|
<div id="projectname">EnTT
|
|
 <span id="projectnumber">3.8.0</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- end header part -->
|
|
<!-- Generated by Doxygen 1.9.1 -->
|
|
<script type="text/javascript">
|
|
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
|
|
var searchBox = new SearchBox("searchBox", "search",false,'Search','.html');
|
|
/* @license-end */
|
|
</script>
|
|
<script type="text/javascript" src="menudata.js"></script>
|
|
<script type="text/javascript" src="menu.js"></script>
|
|
<script type="text/javascript">
|
|
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
|
|
$(function() {
|
|
initMenu('',true,false,'search.php','Search');
|
|
$(document).ready(function() { init_search(); });
|
|
});
|
|
/* @license-end */</script>
|
|
<div id="main-nav"></div>
|
|
<!-- window showing the filter options -->
|
|
<div id="MSearchSelectWindow"
|
|
onmouseover="return searchBox.OnSearchSelectShow()"
|
|
onmouseout="return searchBox.OnSearchSelectHide()"
|
|
onkeydown="return searchBox.OnSearchSelectKey(event)">
|
|
</div>
|
|
|
|
<!-- iframe showing the search results (closed by default) -->
|
|
<div id="MSearchResultsWindow">
|
|
<iframe src="javascript:void(0)" frameborder="0"
|
|
name="MSearchResults" id="MSearchResults">
|
|
</iframe>
|
|
</div>
|
|
|
|
</div><!-- top -->
|
|
<div class="PageDoc"><div class="header">
|
|
<div class="headertitle">
|
|
<div class="title">Crash Course: events, signals and everything in between </div> </div>
|
|
</div><!--header-->
|
|
<div class="contents">
|
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md141"></a>
|
|
Introduction</h1>
|
|
<p>Signals are usually a core part of games and software architectures in general.<br />
|
|
Roughly speaking, they help to decouple the various parts of a system while allowing them to communicate with each other somehow.</p>
|
|
<p>The so called <em>modern C++</em> comes with a tool that can be useful in these terms, the <code>std::function</code>. As an example, it can be used to create delegates.<br />
|
|
However, there is no guarantee that an <code>std::function</code> does not perform allocations under the hood and this could be problematic sometimes. Furthermore, it solves a problem but may not adapt well to other requirements that may arise from time to time.</p>
|
|
<p>In case that the flexibility and power of an <code>std::function</code> isn't required or if the price to pay for them is too high,<code>EnTT</code> offers a complete set of lightweight classes to solve the same and many other problems.</p>
|
|
<h1><a class="anchor" id="autotoc_md142"></a>
|
|
Delegate</h1>
|
|
<p>A delegate can be used as a general purpose invoker with no memory overhead for free functions and members provided along with an instance on which to invoke them.<br />
|
|
It doesn't claim to be a drop-in replacement for an <code>std::function</code>, so don't expect to use it whenever an <code>std::function</code> fits well. That said, it's most likely even a better fit than an <code>std::function</code> in a lot of cases, so expect to use it quite a lot anyway.</p>
|
|
<p>The interface is trivial. It offers a default constructor to create empty delegates:</p>
|
|
<div class="fragment"><div class="line"><a class="code" href="classentt_1_1delegate.html">entt::delegate</a><int(<span class="keywordtype">int</span>)> <a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>{};</div>
|
|
<div class="ttc" id="aclassentt_1_1delegate_html"><div class="ttname"><a href="classentt_1_1delegate.html">entt::delegate</a></div><div class="ttdoc">Basic delegate implementation.</div><div class="ttdef"><b>Definition:</b> <a href="delegate_8hpp_source.html#l00082">delegate.hpp:82</a></div></div>
|
|
<div class="ttc" id="anamespaceentt_html_a7c8907a3a72cd2da043d81f6fd50487a"><div class="ttname"><a href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">entt::delegate</a></div><div class="ttdeci">delegate(connect_arg_t< Candidate >) -> delegate< std::remove_pointer_t< internal::function_pointer_t< decltype(Candidate)>>></div><div class="ttdoc">Deduction guide.</div></div>
|
|
</div><!-- fragment --><p>All what is needed to create an instance is to specify the type of the function the delegate will <em>contain</em>, that is the signature of the free function or the member one wants to assign to it.</p>
|
|
<p>Attempting to use an empty delegate by invoking its function call operator results in undefined behavior or most likely a crash. Before to use a delegate, it must be initialized.<br />
|
|
There exists a bunch of overloads of the <code>connect</code> member function to do that. As an example of use:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">int</span> f(<span class="keywordtype">int</span> i) { <span class="keywordflow">return</span> i; }</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keyword">struct </span>my_struct {</div>
|
|
<div class="line"> <span class="keywordtype">int</span> f(<span class="keyword">const</span> <span class="keywordtype">int</span> &i)<span class="keyword"> const </span>{ <span class="keywordflow">return</span> i; }</div>
|
|
<div class="line">};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// bind a free function to the delegate</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect<&f>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// bind a member function to the delegate</span></div>
|
|
<div class="line">my_struct instance;</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect<&my_struct::f>(instance);</div>
|
|
</div><!-- fragment --><p>The delegate class accepts also data members, if needed. In this case, the function type of the delegate is such that the parameter list is empty and the value of the data member is at least convertible to the return type.</p>
|
|
<p>Free functions having type equivalent to <code>void(T &, args...)</code> are accepted as well. The first argument <code>T &</code> is considered a payload and the function will receive it back every time it's invoked. In other terms, this works just fine with the above definition:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">void</span> g(<span class="keyword">const</span> <span class="keywordtype">char</span> &c, <span class="keywordtype">int</span> i) { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line"><span class="keyword">const</span> <span class="keywordtype">char</span> c = <span class="charliteral">'c'</span>;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect<&g>(c);</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>(42);</div>
|
|
</div><!-- fragment --><p>The function <code>g</code> will be invoked with a reference to <code>c</code> and <code>42</code>. However, the function type of the delegate is still <code>void(int)</code>. This is also the signature of its function call operator.</p>
|
|
<p>Another interesting aspect of the delegate class is that it accepts also functions with a list of parameters that is shorter than that of the function type used to specialize the delegate itself.<br />
|
|
The following code is therefore perfectly valid:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">void</span> g() { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect<&g>();</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>(42);</div>
|
|
</div><!-- fragment --><p>Where the function type of the delegate is <code>void(int)</code> as above. It goes without saying that the extra arguments are silently discarded internally.<br />
|
|
This is a nice-to-have feature in a lot of cases, as an example when the <code>delegate</code> class is used as a building block of a signal-slot system.</p>
|
|
<p>To create and initialize a delegate at once, there are a few specialized constructors. Because of the rules of the language, the listener is provided by means of the <code><a class="el" href="namespaceentt.html#af1e73acd6f3d892955819677dc2664b7" title="Constant of type connect_arg_t used to disambiguate calls.">entt::connect_arg</a></code> variable template:</p>
|
|
<div class="fragment"><div class="line"><a class="code" href="classentt_1_1delegate.html">entt::delegate</a><int(<span class="keywordtype">int</span>)> func{entt::connect_arg<&f>};</div>
|
|
</div><!-- fragment --><p>Aside <code>connect</code>, a <code>disconnect</code> counterpart isn't provided. Instead, there exists a <code>reset</code> member function to use to clear a delegate.<br />
|
|
To know if a delegate is empty, it can be used explicitly in every conditional statement:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordflow">if</span>(delegate) {</div>
|
|
<div class="line"> <span class="comment">// ...</span></div>
|
|
<div class="line">}</div>
|
|
</div><!-- fragment --><p>Finally, to invoke a delegate, the function call operator is the way to go as already shown in the examples above:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">auto</span> ret = <a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>(42);</div>
|
|
</div><!-- fragment --><p>In all cases, the listeners don't have to strictly follow the signature of the delegate. As long as a listener can be invoked with the given arguments to yield a result that is convertible to the given result type, everything works just fine.</p>
|
|
<p>As a side note, members of classes may or may not be associated with instances. If they are not, the first argument of the function type must be that of the class on which the members operate and an instance of this class must obviously be passed when invoking the delegate:</p>
|
|
<div class="fragment"><div class="line"><a class="code" href="classentt_1_1delegate.html">entt::delegate</a><void(my_struct &, <span class="keywordtype">int</span>)> <a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>;</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect<&my_struct::f>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line">my_struct instance;</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>(instance, 42);</div>
|
|
</div><!-- fragment --><p>In this case, it's not possible to deduce the function type since the first argument doesn't necessarily have to be a reference (for example, it can be a pointer, as well as a const reference).<br />
|
|
Therefore, the function type must be declared explicitly for unbound members.</p>
|
|
<h2><a class="anchor" id="autotoc_md143"></a>
|
|
Runtime arguments</h2>
|
|
<p>The <code>delegate</code> class is meant to be used primarily with template arguments. However, as a consequence of its design, it can also offer minimal support for runtime arguments.<br />
|
|
When used in this modality, some feature aren't supported though. In particular:</p>
|
|
<ul>
|
|
<li>Curried functions aren't accepted.</li>
|
|
<li>Functions with an argument list that differs from that of the delegate aren't supported.</li>
|
|
<li>Return type and types of arguments <b>must</b> coincide with those of the delegate and <em>being at least convertible</em> isn't enough anymore.</li>
|
|
</ul>
|
|
<p>Moreover, for a given function type <code>Ret(Args...)</code>, the signature of the functions connected at runtime must necessarily be <code>Ret(const void *, Args...)</code>.</p>
|
|
<p>Runtime arguments can be passed both to the constructor of a delegate and to the <code>connect</code> member function. An optional parameter is also accepted in both cases. This argument is used to pass arbitrary user data back and forth as a <code>const void *</code> upon invocation.<br />
|
|
To connect a function to a delegate <em>in the hard way</em>:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">int</span> func(<span class="keyword">const</span> <span class="keywordtype">void</span> *ptr, <span class="keywordtype">int</span> i) { <span class="keywordflow">return</span> *<span class="keyword">static_cast<</span><span class="keyword">const </span><span class="keywordtype">int</span> *<span class="keyword">></span>(ptr) * i; }</div>
|
|
<div class="line"><span class="keyword">const</span> <span class="keywordtype">int</span> value = 42;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// use the constructor ...</span></div>
|
|
<div class="line"><a class="code" href="classentt_1_1delegate.html">entt::delegate</a> <a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>{&func, &value};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ... or the connect member function</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect(&func, &value);</div>
|
|
</div><!-- fragment --><p>The type of the delegate is deduced from the function if possible. In this case, since the first argument is an implementation detail, the deduced function type is <code>int(int)</code>.<br />
|
|
Invoking a delegate built in this way follows the same rules as previously explained.</p>
|
|
<h2><a class="anchor" id="autotoc_md144"></a>
|
|
Lambda support</h2>
|
|
<p>In general, the <code>delegate</code> class doesn't fully support lambda functions in all their nuances. The reason is pretty simple: a <code>delegate</code> isn't a drop-in replacement for an <code>std::function</code>. Instead, it tries to overcome the problems with the latter.<br />
|
|
That being said, non-capturing lambda functions are supported, even though some feature aren't available in this case.</p>
|
|
<p>This is a logical consequence of the support for connecting functions at runtime. Therefore, lambda functions undergo the same rules and limitations.<br />
|
|
In fact, since non-capturing lambda functions decay to pointers to functions, they can be used with a <code>delegate</code> as if they were <em>normal functions</em> with optional payload:</p>
|
|
<div class="fragment"><div class="line">my_struct instance;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// use the constructor ...</span></div>
|
|
<div class="line"><a class="code" href="classentt_1_1delegate.html">entt::delegate</a> <a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>{+[](<span class="keyword">const</span> <span class="keywordtype">void</span> *ptr, <span class="keywordtype">int</span> value) {</div>
|
|
<div class="line"> <span class="keywordflow">return</span> <span class="keyword">static_cast<</span><span class="keyword">const </span>my_struct *<span class="keyword">></span>(ptr)->f(value);</div>
|
|
<div class="line">}, &instance};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ... or the connect member function</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#a7c8907a3a72cd2da043d81f6fd50487a">delegate</a>.connect([](<span class="keyword">const</span> <span class="keywordtype">void</span> *ptr, <span class="keywordtype">int</span> value) {</div>
|
|
<div class="line"> <span class="keywordflow">return</span> <span class="keyword">static_cast<</span><span class="keyword">const </span>my_struct *<span class="keyword">></span>(ptr)->f(value);</div>
|
|
<div class="line">}, &instance);</div>
|
|
</div><!-- fragment --><p>As above, the first parameter (<code>const void *</code>) isn't part of the function type of the delegate and is used to dispatch arbitrary user data back and forth. In other terms, the function type of the delegate above is <code>int(int)</code>.</p>
|
|
<h1><a class="anchor" id="autotoc_md145"></a>
|
|
Signals</h1>
|
|
<p>Signal handlers work with references to classes, function pointers and pointers to members. Listeners can be any kind of objects and users are in charge of connecting and disconnecting them from a signal to avoid crashes due to different lifetimes. On the other side, performance shouldn't be affected that much by the presence of such a signal handler.<br />
|
|
Signals make use of delegates internally and therefore they undergo the same rules and offer similar functionalities. It may be a good idea to consult the documentation of the <code>delegate</code> class for further information.</p>
|
|
<p>A signal handler can be used as a private data member without exposing any <em>publish</em> functionality to the clients of a class. The basic idea is to impose a clear separation between the signal itself and the <code>sink</code> class, that is a tool to be used to connect and disconnect listeners on the fly.</p>
|
|
<p>The API of a signal handler is straightforward. If a collector is supplied to the signal when something is published, all the values returned by the listeners can be literally <em>collected</em> and used later by the caller. Otherwise, the class works just like a plain signal that emits events from time to time.<br />
|
|
To create instances of signal handlers it is sufficient to provide the type of function to which they refer:</p>
|
|
<div class="fragment"><div class="line"><a class="code" href="classentt_1_1sigh.html">entt::sigh</a><void(<span class="keywordtype">int</span>, <span class="keywordtype">char</span>)> signal;</div>
|
|
<div class="ttc" id="aclassentt_1_1sigh_html"><div class="ttname"><a href="classentt_1_1sigh.html">entt::sigh</a></div><div class="ttdoc">Unmanaged signal handler.</div><div class="ttdef"><b>Definition:</b> <a href="signal_2fwd_8hpp_source.html#l00030">fwd.hpp:30</a></div></div>
|
|
</div><!-- fragment --><p>Signals offer all the basic functionalities required to know how many listeners they contain (<code>size</code>) or if they contain at least a listener (<code>empty</code>), as well as a function to use to swap handlers (<code>swap</code>).</p>
|
|
<p>Besides them, there are member functions to use both to connect and disconnect listeners in all their forms by means of a sink:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">void</span> foo(<span class="keywordtype">int</span>, <span class="keywordtype">char</span>) { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keyword">struct </span>listener {</div>
|
|
<div class="line"> <span class="keywordtype">void</span> bar(<span class="keyword">const</span> <span class="keywordtype">int</span> &, <span class="keywordtype">char</span>) { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line">};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code" href="classentt_1_1sink.html">entt::sink</a> <a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>{signal};</div>
|
|
<div class="line">listener instance;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.connect<&foo>();</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.connect<&listener::bar>(instance);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// disconnects a free function</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.disconnect<&foo>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// disconnect a member function of an instance</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.disconnect<&listener::bar>(instance);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// disconnect all member functions of an instance, if any</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.disconnect(instance);</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// discards all listeners at once</span></div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.disconnect();</div>
|
|
<div class="ttc" id="aclassentt_1_1sink_html"><div class="ttname"><a href="classentt_1_1sink.html">entt::sink</a></div><div class="ttdoc">Sink class.</div><div class="ttdef"><b>Definition:</b> <a href="signal_2fwd_8hpp_source.html#l00026">fwd.hpp:26</a></div></div>
|
|
<div class="ttc" id="anamespaceentt_html_ad3c393cd14012db79a3bbabd816e46b3"><div class="ttname"><a href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">entt::sink</a></div><div class="ttdeci">sink(sigh< Ret(Args...)> &) -> sink< Ret(Args...)></div><div class="ttdoc">Deduction guide.</div></div>
|
|
</div><!-- fragment --><p>As shown above, the listeners don't have to strictly follow the signature of the signal. As long as a listener can be invoked with the given arguments to yield a result that is convertible to the given return type, everything works just fine.<br />
|
|
It's also possible to connect a listener before other listeners already contained by the signal. The <code>before</code> function returns a <code>sink</code> object correctly initialized for the purpose that can be used to connect one or more listeners in order and in the desired position:</p>
|
|
<div class="fragment"><div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.before<&foo>().connect<&listener::bar>(instance);</div>
|
|
</div><!-- fragment --><p>In all cases, the <code>connect</code> member function returns by default a <code>connection</code> object to be used as an alternative to break a connection by means of its <code>release</code> member function. A <code>scoped_connection</code> can also be created from a connection. In this case, the link is broken automatically as soon as the object goes out of scope.</p>
|
|
<p>Once listeners are attached (or even if there are no listeners at all), events and data in general can be published through a signal by means of the <code>publish</code> member function:</p>
|
|
<div class="fragment"><div class="line">signal.publish(42, <span class="charliteral">'c'</span>);</div>
|
|
</div><!-- fragment --><p>To collect data, the <code>collect</code> member function should be used instead. Below is a minimal example to show how to use it:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">int</span> f() { <span class="keywordflow">return</span> 0; }</div>
|
|
<div class="line"><span class="keywordtype">int</span> g() { <span class="keywordflow">return</span> 1; }</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code" href="classentt_1_1sigh.html">entt::sigh</a><int()> signal;</div>
|
|
<div class="line"><a class="code" href="classentt_1_1sink.html">entt::sink</a> <a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>{signal};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.connect<&f>();</div>
|
|
<div class="line"><a class="code" href="namespaceentt.html#ad3c393cd14012db79a3bbabd816e46b3">sink</a>.connect<&g>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line">std::vector<int> vec{};</div>
|
|
<div class="line">signal.collect([&vec](<span class="keywordtype">int</span> value) { vec.push_back(value); });</div>
|
|
<div class="line"> </div>
|
|
<div class="line">assert(vec[0] == 0);</div>
|
|
<div class="line">assert(vec[1] == 1);</div>
|
|
</div><!-- fragment --><p>A collector must expose a function operator that accepts as an argument a type to which the return type of the listeners can be converted. Moreover, it can optionally return a boolean value that is true to stop collecting data, false otherwise. This way one can avoid calling all the listeners in case it isn't necessary.<br />
|
|
Functors can also be used in place of a lambda. Since the collector is copied when invoking the <code>collect</code> member function, <code>std::ref</code> is the way to go in this case:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_collector {</div>
|
|
<div class="line"> std::vector<int> vec{};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"> <span class="keywordtype">bool</span> operator()(<span class="keywordtype">int</span> v) noexcept {</div>
|
|
<div class="line"> vec.push_back(v);</div>
|
|
<div class="line"> <span class="keywordflow">return</span> <span class="keyword">true</span>;</div>
|
|
<div class="line"> }</div>
|
|
<div class="line">};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line">my_collector <a class="code" href="namespaceentt.html#aea8b073b774362fe800d9cce3542927a">collector</a>;</div>
|
|
<div class="line">signal.collect(std::ref(collector));</div>
|
|
<div class="ttc" id="anamespaceentt_html_aea8b073b774362fe800d9cce3542927a"><div class="ttname"><a href="namespaceentt.html#aea8b073b774362fe800d9cce3542927a">entt::collector</a></div><div class="ttdeci">constexpr basic_collector collector</div><div class="ttdoc">Variable template used to ease the definition of collectors.</div><div class="ttdef"><b>Definition:</b> <a href="observer_8hpp_source.html#l00119">observer.hpp:119</a></div></div>
|
|
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md146"></a>
|
|
Event dispatcher</h1>
|
|
<p>The event dispatcher class is designed so as to be used in a loop. It allows users both to trigger immediate events or to queue events to be published all together once per tick.<br />
|
|
This class shares part of its API with the one of the signal handler, but it doesn't require that all the types of events are specified when declared:</p>
|
|
<div class="fragment"><div class="line"><span class="comment">// define a general purpose dispatcher</span></div>
|
|
<div class="line"><a class="code" href="classentt_1_1dispatcher.html">entt::dispatcher</a> dispatcher{};</div>
|
|
<div class="ttc" id="aclassentt_1_1dispatcher_html"><div class="ttname"><a href="classentt_1_1dispatcher.html">entt::dispatcher</a></div><div class="ttdoc">Basic dispatcher implementation.</div><div class="ttdef"><b>Definition:</b> <a href="dispatcher_8hpp_source.html#l00031">dispatcher.hpp:31</a></div></div>
|
|
</div><!-- fragment --><p>In order to register an instance of a class to a dispatcher, its type must expose one or more member functions the arguments of which are such that <code>E &</code> can be converted to them for each type of event <code>E</code>, no matter what the return value is.<br />
|
|
The name of the member function aimed to receive the event must be provided to the <code>connect</code> member function of the sink in charge for the specific event:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>an_event { <span class="keywordtype">int</span> value; };</div>
|
|
<div class="line"><span class="keyword">struct </span>another_event {};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="keyword">struct </span>listener {</div>
|
|
<div class="line"> <span class="keywordtype">void</span> receive(<span class="keyword">const</span> an_event &) { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line"> <span class="keywordtype">void</span> method(<span class="keyword">const</span> another_event &) { <span class="comment">/* ... */</span> }</div>
|
|
<div class="line">};</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line">listener listener;</div>
|
|
<div class="line">dispatcher.sink<an_event>().connect<&listener::receive>(listener);</div>
|
|
<div class="line">dispatcher.sink<another_event>().connect<&listener::method>(listener);</div>
|
|
</div><!-- fragment --><p>The <code>disconnect</code> member function follows the same pattern and can be used to remove one listener at a time or all of them at once:</p>
|
|
<div class="fragment"><div class="line">dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);</div>
|
|
<div class="line">dispatcher.sink<another_event>().disconnect(listener);</div>
|
|
</div><!-- fragment --><p>The <code>trigger</code> member function serves the purpose of sending an immediate event to all the listeners registered so far. It offers a convenient approach that relieves users from having to create the event itself. Instead, it's enough to specify the type of event and provide all the parameters required to construct it.<br />
|
|
As an example:</p>
|
|
<div class="fragment"><div class="line">dispatcher.trigger<an_event>(42);</div>
|
|
<div class="line">dispatcher.trigger<another_event>();</div>
|
|
</div><!-- fragment --><p>Listeners are invoked immediately, order of execution isn't guaranteed. This method can be used to push around urgent messages like an <em>is terminating</em> notification on a mobile app.</p>
|
|
<p>On the other hand, the <code>enqueue</code> member function queues messages together and allows to maintain control over the moment they are sent to listeners. The signature of this method is more or less the same of <code>trigger</code>:</p>
|
|
<div class="fragment"><div class="line">dispatcher.enqueue<an_event>(42);</div>
|
|
<div class="line">dispatcher.enqueue<another_event>();</div>
|
|
</div><!-- fragment --><p>Events are stored aside until the <code>update</code> member function is invoked, then all the messages that are still pending are sent to the listeners at once:</p>
|
|
<div class="fragment"><div class="line"><span class="comment">// emits all the events of the given type at once</span></div>
|
|
<div class="line">dispatcher.update<an_event>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// emits all the events queued so far at once</span></div>
|
|
<div class="line">dispatcher.update();</div>
|
|
</div><!-- fragment --><p>This way users can embed the dispatcher in a loop and literally dispatch events once per tick to their systems.</p>
|
|
<h1><a class="anchor" id="autotoc_md147"></a>
|
|
Event emitter</h1>
|
|
<p>A general purpose event emitter thought mainly for those cases where it comes to working with asynchronous stuff.<br />
|
|
Originally designed to fit the requirements of <a href="https://github.com/skypjack/uvw"><code>uvw</code></a> (a wrapper for <code>libuv</code> written in modern C++), it was adapted later to be included in this library.</p>
|
|
<p>To create a custom emitter type, derived classes must inherit directly from the base class as:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_emitter: emitter<my_emitter> {</div>
|
|
<div class="line"> <span class="comment">// ...</span></div>
|
|
<div class="line">}</div>
|
|
</div><!-- fragment --><p>The full list of accepted types of events isn't required. Handlers are created internally on the fly and thus each type of event is accepted by default.</p>
|
|
<p>Whenever an event is published, an emitter provides the listeners with a reference to itself along with a reference to the event. Therefore listeners have an handy way to work with it without incurring in the need of capturing a reference to the emitter itself.<br />
|
|
In addition, an opaque object is returned each time a connection is established between an emitter and a listener, allowing the caller to disconnect them at a later time.<br />
|
|
The opaque object used to handle connections is both movable and copyable. On the other side, an event emitter is movable but not copyable by default.</p>
|
|
<p>To create new instances of an emitter, no arguments are required:</p>
|
|
<div class="fragment"><div class="line">my_emitter emitter{};</div>
|
|
</div><!-- fragment --><p>Listeners must be movable and callable objects (free functions, lambdas, functors, <code>std::function</code>s, whatever) whose function type is compatible with:</p>
|
|
<div class="fragment"><div class="line">void(Event &, my_emitter &)</div>
|
|
</div><!-- fragment --><p>Where <code>Event</code> is the type of event they want to listen.<br />
|
|
There are two ways to attach a listener to an event emitter that differ slightly from each other:</p>
|
|
<ul>
|
|
<li>To register a long-lived listener, use the <code>on</code> member function. It is meant to register a listener designed to be invoked more than once for the given event type.<br />
|
|
As an example:</li>
|
|
</ul>
|
|
<div class="fragment"><div class="line"><span class="keyword">auto</span> conn = emitter.on<my_event>([](<span class="keyword">const</span> my_event &event, my_emitter &emitter) {</div>
|
|
<div class="line"> <span class="comment">// ...</span></div>
|
|
<div class="line">});</div>
|
|
</div><!-- fragment --><p>The connection object can be freely discarded. Otherwise, it can be used later to disconnect the listener if required.</p>
|
|
<ul>
|
|
<li>To register a short-lived listener, use the <code>once</code> member function. It is meant to register a listener designed to be invoked only once for the given event type. The listener is automatically disconnected after the first invocation.<br />
|
|
As an example:</li>
|
|
</ul>
|
|
<div class="fragment"><div class="line"><span class="keyword">auto</span> conn = emitter.once<my_event>([](<span class="keyword">const</span> my_event &event, my_emitter &emitter) {</div>
|
|
<div class="line"> <span class="comment">// ...</span></div>
|
|
<div class="line">});</div>
|
|
</div><!-- fragment --><p>The connection object can be freely discarded. Otherwise, it can be used later to disconnect the listener if required.</p>
|
|
<p>In both cases, the connection object can be used with the <code>erase</code> member function:</p>
|
|
<div class="fragment"><div class="line">emitter.erase(conn);</div>
|
|
</div><!-- fragment --><p>There are also two member functions to use either to disconnect all the listeners for a given type of event or to clear the emitter:</p>
|
|
<div class="fragment"><div class="line"><span class="comment">// removes all the listener for the specific event</span></div>
|
|
<div class="line">emitter.clear<my_event>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// removes all the listeners registered so far</span></div>
|
|
<div class="line">emitter.clear();</div>
|
|
</div><!-- fragment --><p>To send an event to all the listeners that are interested in it, the <code>publish</code> member function offers a convenient approach that relieves users from having to create the event:</p>
|
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>my_event { <span class="keywordtype">int</span> i; };</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// ...</span></div>
|
|
<div class="line"> </div>
|
|
<div class="line">emitter.publish<my_event>(42);</div>
|
|
</div><!-- fragment --><p>Finally, the <code>empty</code> member function tests if there exists at least either a listener registered with the event emitter or to a given type of event:</p>
|
|
<div class="fragment"><div class="line"><span class="keywordtype">bool</span> empty;</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// checks if there is any listener registered for the specific event</span></div>
|
|
<div class="line">empty = emitter.empty<my_event>();</div>
|
|
<div class="line"> </div>
|
|
<div class="line"><span class="comment">// checks it there are listeners registered with the event emitter</span></div>
|
|
<div class="line">empty = emitter.empty();</div>
|
|
</div><!-- fragment --><p>In general, the event emitter is a handy tool when the derived classes <em>wrap</em> asynchronous operations, because it introduces a <em>nice-to-have</em> model based on events and listeners that kindly hides the complexity behind the scenes. However it is not limited to such uses. </p>
|
|
</div></div><!-- contents -->
|
|
</div><!-- PageDoc -->
|
|
<!-- start footer part -->
|
|
<hr class="footer"/><address class="footer"><small>
|
|
Generated by <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.1
|
|
</small></address>
|
|
</body>
|
|
</html>
|