Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There are two approaches for callbacks, either you provide an interface that has a virtual function that will be called, or else you perform <em>type-erasure</em> to make it fully generic. In the second version you either use a prepacked solution for the <em>type-erasure</em> or you will need to use some templates to implement it yourself.</p> <p>The expected user code that you show in the first block follows the first approach, and is sufficient for the basic needs. But note that you will probably want to use pointers or references in a couple of the places where you are passing by value:</p> <pre><code>// user code Interactor dial; DialEventListener dial_listener; dial.addListener( ROTATE_EVENT, dial_listener ); // implementation void Interactor::addListener( EventType event, EventListener &amp; listener ) { Signal1&lt;TUIKitEvent&gt; &amp; sgn = listener_map[event]; sgn.Connect( &amp;listener, &amp;EventListener::handleEvent ); }; </code></pre> <p>Note a couple of changes: the <code>listener</code> argument is passed by reference rather than value. If you pass by value you will get <em>slicing</em> (only the <code>EventListener</code> subobject will be copied, and it will not be a <code>DialEventListener</code> object inside <code>addListener</code>. By passing a reference to the base type, you can use it as a base while the object maintains its identity.</p> <p>That in turn requires that the object you pass in is not a temporary (you cannot use <code>DialEventListener()</code> as second argument to <code>addListener</code>, as the lifetime of the temporary will end at the end of the full expression and that means you will have a dangling reference (in your current implementation you have a dangling pointer inside the signal implementation, which will most probably cause UB once the signal is triggered).</p> <p>Additionally, inside <code>addListener</code> you don't want to <em>copy</em> the signal stored in the map, but rather modify it, so again, you should use a reference rather than a value when accessing the signal in the map.</p> <p>The documentation of your class should be quite explicit in that the lifetime of the object passed in must outlive the <code>Interactor</code> (or else allow for <em>deregistration</em> so that the client can remove the handler from the <code>Interactor</code> before the callback is destroyed (again, if you don't you will end in UB)</p> <p>The other approach is performing <em>type-erasure</em>, I have never used that particular signals library so I am playing by ear here. The signal library that you are using (probably) implements type-erasure internally, but you need to be able to forward the exact type until the lib can perform the erasure, or else use a different type erasure library in the interface.</p> <pre><code>// Transferring the exact type to the signal lib: class Interactor { // ... public: template &lt;typename Listener&gt; void addListener( EventType event, Listener &amp; listener, void (Listener::*callback)(Event) ) { Signal1&lt;TUIKitEvent&gt; &amp; sgn = listener_map[event]; sgn.connect( &amp;listener, callback ); } //... </code></pre> <p>Using a different type erasure library in the interface might or not be possible depending on the semantics of the <code>connect</code> method in your signals implementation. If it copies the first argument, then you can probably make do with <code>std::function</code>:</p> <pre><code>class Interactor { //... public: void addListener( EventType event, std::function&lt; void (Event) &gt; callback ) { Signal1&lt;TUIKitEvent&gt; &amp; sgn = listener_map[event]; sgn.connect( callback, &amp;std::function&lt;void(Event)&gt;::operator() ); } //... }; </code></pre> <p>Assuming that the library will <em>copy</em> the first argument and maintain it's own copy internally (i.e. assuming that it behaves somehow like <code>boost::signal</code> library). On the user side, they will have to perform the type erasure:</p> <pre><code>struct AnotherListener { // no need to explicitly derive from EventListner now! void method( Event e ) {} }; int main() { Interactor dial; AnotherEventListener listener; dial.addListener( dial, std::bind( &amp;AnotherListener::method, &amp;listener ) ); } </code></pre> <p>The exact syntax of <code>bind</code> might not be that, I have never used it from the new standard in the boost library it would have been <code>boost::bind( &amp;AnotherListener::method, &amp;listener, _1 );</code>...</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload