<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[c++0x variadic templates - Message&#x2F;Event System]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich habe in letzter Zeit ein (vermutlich?) von mir erdachtes pattern für message systems in c++ angewandt, das ich hier mal vorstellen wollte (ich hoffe natürlich auch auf feedback/Verbesserungsvorschläge).<br />
Es geht, wie der Titel schon sagt, um ein message system in c++ zur Kommunikation zwischen Objekten. Um zu zeigen, was ich unter &quot;message system&quot; verstehe, erst der klassische Ansatz:<br />
Es gibt eine Basisklasse &quot;Listener&quot;, die bestimmte virtuelle Funktionen bereitstellt, in etwa so (ein fiktives Beispiel für einen Listener auf Mausinput Events):</p>
<pre><code class="language-cpp">class MouseListener
{
public:
    virtual void leftButtonDown( unsigned int XPos , unsigned int YPos ) {}
    virtual void rightButtonDown( unsigned int XPos , unsigned int YPos ) {}
    virtual void leftButtonUp( unsigned int XPos , unsigned int YPos ) {}
    //...
};
</code></pre>
<p>Ein Klasse kann dann von diesem Listener erben und die jeweiligen Funktionen überschreiben:</p>
<pre><code class="language-cpp">class MyListener
    : public MouseListener
{
public:
    void leftButtonDown( unsigned int XPos , unsigned int YPos ) { /* irgendetwas sinnvolles tun */ }
};
</code></pre>
<p>Dann kann man einen MyListener dem MouseInput übergeben, der MouseInput an jeden MouseListener verteilt:</p>
<pre><code class="language-cpp">MouseInput mouse_input;

int main()
{
    MyListener listener;
    mouse_input.addListener( listener );
}
</code></pre>
<p>Dieses pattern funktioniert auch fast immer. Ich finde es aber nicht ganz optimal: Angenommen, man will einen MouseListener, der alle ButtonDown - Events mitzählt, unabhängig von der Taste. Man müsste jetzt jede &quot;...ButtonDown&quot; - Methode überschrieben, nur um jedes Mal exakt den gleichen code auszuführen. Das ist zweifelsohne möglich, aber was passiert, wenn man während der Entwicklung auf die Idee kommt, auch andere Tasten zu unterstützen? Man müsste dem MouseListener-Interface eine neue Funktion hinzufügen, etwa &quot;middleButtonDown&quot;. Dann müsste man in jeder Klasse, die von MouseListener erbt, prüfen, ob der code so noch funktioniert (was er im Fall des Klick-Counters nicht tut), was leicht zu bugs führen kann.<br />
Wenn man sich jetzt auch noch vorstellt, dass man das Gleiche mit einem KeyboardListener machen müsste, der Methoden wie &quot;AKeyDown&quot; bereitstellt, läuft es vermutlich jedem von uns kalt den Rücken runter.</p>
<p>Die Lösung ist Vererbung. Man könnte eine Basis-Message für &quot;ButtonDown&quot; - Events erstellen, von der dann alle &quot;...ButtonDown&quot; - Events erben, etwa so:</p>
<pre><code class="language-cpp">struct ButtonDown
{
    unsigned int XPos;
    unsigned int YPos;
};

struct LeftButtonDown
    : public ButtonDown
{
};

struct RightButtonDown
	: public ButtonDown
{
};
</code></pre>
<p>Der Weg vom abstrakten Listener zum eigentlich Handler ist aber alles andere als offensichtlich (zumindest war er das nicht für mich).<br />
Zuerst muss durch virtual Functions jede Message auf die Ebene des Handlers kommen. Danach muss noch irgendwie statt der 1:1 virtual Function calls die normale overload resolution verwendet werden.</p>
<p>Jetzt aber zuerst die Listener - Klasse:</p>
<pre><code class="language-cpp">template&lt; class ...AllMessageTypes &gt;
class Listener;

template&lt;&gt;
class Listener&lt;&gt;
{
private:
    struct MessageDummy;
public:
    void listen( const MessageDummy &amp; );
    //von dieser Klasse wird abgeleitet, um das Listener&lt; ... &gt; - Interface zu benutzen
    template&lt; class HandlerType &gt; //HandlerType ist der Typ der Klasse, die von Handler ableitet
    class Handler;
};

template&lt; class Head , class ...Tail &gt;
class Listener&lt; Head , Tail... &gt;
    : public Listener&lt; Tail... &gt;
{
public:
    virtual void listen( const Head &amp; ) = 0;
    using Listener&lt; Tail... &gt;::listen;
    template&lt; class HandlerType &gt;
    class Handler;
};
</code></pre>
<p>So weit so einfach. Jetzt kommt der schwierigere Teil: Wir müssen zum einen alle rein virtuellen Funktionen überschrieben, andererseits müssen wir immernoch irgendwie Funktionsüberladung verwenden. Ersteres ist wieder mit variadic templates möglich, für den zweiten Teil werden wir SFINAE (-&gt; <a href="http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error" rel="nofollow">http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error</a> ) verwenden.</p>
<p>Zur Verwendung von SFINAE:</p>
<pre><code class="language-cpp">template&lt; class &gt;
class VoidWrapper
{
public:
    typedef void Void;
};

template&lt; class HandlerType , class MessageType &gt;
auto handleIfPossible( HandlerType *Handler , const MessageType *Message ) -&gt; typename VoidWrapper&lt; decltype( Handler-&gt;handle( *Message ) ) &gt;::Void
//Fehler, wenn Handler-&gt;handle( *Message ) nicht möglich ist
{
   Handler-&gt;handle( *Message );
};

template&lt; class HandlerType , class MessageType &gt;
void handleIfPossible( ... )
//schwächer als die andere Version, wegen Ellipse
{
}
</code></pre>
<p>Jetzt kommen wir zur Verwendung von varidadic templates zum Überschreiben der &quot;listen&quot;-Funktionen</p>
<pre><code class="language-cpp">template&lt; class ... &gt;
class TypeList;

template&lt; class HandlerType , class StaticListOfAllMessages , class IterationListOfAllMessages &gt;
class ListenOverwriter;

template&lt; class HandlerType , class ...AllMessageTypes &gt;
class ListenOverwriter&lt; HandlerType , TypeList&lt; AllMessageTypes... &gt; , TypeList&lt;&gt; &gt;
    : public Listener&lt; AllMessageTypes... &gt;
{
protected:
    HandlerType *handler;
};

template&lt; class HandlerType , class SetOfAllMessages , class Head , class ...Tail &gt;
class ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Head , Tail... &gt; &gt;
    : public ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Tail... &gt; &gt;
{
protected:
    using ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Tail... &gt; &gt;::handler;
    void listen( const Head &amp;Message )
    {
        handleIfPossible&lt; HandlerType , Head &gt;( handler , &amp;Message );
    }
};

//Eigentlich müsste man auch noch für den Fall bei keiner Message Handler definieren. Das lasse ich der Einfachheit halber jetzt weg
template&lt; class HeadMessageType , class ...Tail &gt; template&lt; class HandlerType &gt;
class Listener&lt; HeadMessageType , Tail... &gt;::Handler
	: public ListenOverwriter&lt; HandlerType , TypeList&lt; HeadMessageType , Tail... &gt; , TypeList&lt; HeadMessageType , Tail... &gt; &gt;
{
private:
	using ListenOverwriter&lt; HandlerType , TypeList&lt; HeadMessageType , Tail... &gt; , TypeList&lt; HeadMessageType , Tail... &gt; &gt;::handler;
public:
	Handler( HandlerType &amp;HandlingObject )
	{
		handler = &amp;HandlingObject;
	}
};
</code></pre>
<p>Das wars auch schon!<br />
Verwendet werden kann dieses System nun so (ich verwende wieder die Message von weiter oben):</p>
<pre><code class="language-cpp">typedef Listener&lt; ButtonDown , LeftButtonDown , RightButtonDown &gt; MouseListener;

template&lt; class HandlingType &gt;
class MouseHandler
    : public MouseListener::Handler&lt; HandlingType &gt;
{
public:
    MouseHandler( HandlingType &amp;HandlingObject )
        : MouseListener::Handler&lt; HandlingType &gt;( HandlingObject )
    {
    }
};

class ButtonDownCounter
   : public MouseHandler&lt; ButtonDownCounter &gt;
{
private:
    unsigned int count;
public:
    ButtonDownCounter()
        : MouseHandler&lt; ButtonDownCounter &gt;( *this )
    {
        count = 0;
    }
    void handle( const LeftButtonDown &amp; )
    {
        ++count;
        std::cout&lt;&lt; &quot;LeftButtonDown&quot; &lt;&lt;std::endl;
    }
    void handle( const ButtonDown &amp; )
    {
        ++count;
        std::cout&lt;&lt; &quot;AnyOtherButtonDown&quot;&lt;&lt;std::endl;
    }
};

void sendMessagesTo( MouseListener &amp;ListeningObject )
{
	ListeningObject.listen( LeftButtonDown() );
	ListeningObject.listen( RightButtonDown() );
}

void test()
{
	ButtonDownCounter counter;
	sendMessagesTo( counter );
}
</code></pre>
<p>Dieses Pattern ist also ähnlich einfach zu benutzen wie die Lehrbuch-Variante (naja, vielleicht etwas komplizierter).<br />
Durch diesen message-dispatch Mechanismus sind auch noch andere features denkbar. So ist es etwa möglich, alle Messages an einen Listener zu geben, dessen (polymorpher) Handler diese Messages wiederum an Objekte mit zur Compilezeit veststehende Objekte verteilt. Dadurch kann man mit dem Overhead von nur 1 virtual function call pro Message an beliebig viele Typen verteilen.<br />
Dadurch, dass die Daten der einzelnen Events jetzt in structs stecken, könnte man eine eine message queue bauen, die Messages zwischenspeichert (das ist aber alles andere als trivial).</p>
<p>So, ich hoffe ihr habt bis zum Schluss durchgehalten, ist jetzt doch etwas länger geworden, als ich dachte <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title="=)"
      alt="🙂"
    /><br />
Über feedback und Kritik würde ich mich sehr freuen.</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/291860/c-0x-variadic-templates-message-event-system</link><generator>RSS for Node</generator><lastBuildDate>Mon, 17 Aug 2026 13:03:10 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/291860.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 29 Aug 2011 01:04:12 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to c++0x variadic templates - Message&#x2F;Event System on Mon, 29 Aug 2011 01:04:12 GMT]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich habe in letzter Zeit ein (vermutlich?) von mir erdachtes pattern für message systems in c++ angewandt, das ich hier mal vorstellen wollte (ich hoffe natürlich auch auf feedback/Verbesserungsvorschläge).<br />
Es geht, wie der Titel schon sagt, um ein message system in c++ zur Kommunikation zwischen Objekten. Um zu zeigen, was ich unter &quot;message system&quot; verstehe, erst der klassische Ansatz:<br />
Es gibt eine Basisklasse &quot;Listener&quot;, die bestimmte virtuelle Funktionen bereitstellt, in etwa so (ein fiktives Beispiel für einen Listener auf Mausinput Events):</p>
<pre><code class="language-cpp">class MouseListener
{
public:
    virtual void leftButtonDown( unsigned int XPos , unsigned int YPos ) {}
    virtual void rightButtonDown( unsigned int XPos , unsigned int YPos ) {}
    virtual void leftButtonUp( unsigned int XPos , unsigned int YPos ) {}
    //...
};
</code></pre>
<p>Ein Klasse kann dann von diesem Listener erben und die jeweiligen Funktionen überschreiben:</p>
<pre><code class="language-cpp">class MyListener
    : public MouseListener
{
public:
    void leftButtonDown( unsigned int XPos , unsigned int YPos ) { /* irgendetwas sinnvolles tun */ }
};
</code></pre>
<p>Dann kann man einen MyListener dem MouseInput übergeben, der MouseInput an jeden MouseListener verteilt:</p>
<pre><code class="language-cpp">MouseInput mouse_input;

int main()
{
    MyListener listener;
    mouse_input.addListener( listener );
}
</code></pre>
<p>Dieses pattern funktioniert auch fast immer. Ich finde es aber nicht ganz optimal: Angenommen, man will einen MouseListener, der alle ButtonDown - Events mitzählt, unabhängig von der Taste. Man müsste jetzt jede &quot;...ButtonDown&quot; - Methode überschrieben, nur um jedes Mal exakt den gleichen code auszuführen. Das ist zweifelsohne möglich, aber was passiert, wenn man während der Entwicklung auf die Idee kommt, auch andere Tasten zu unterstützen? Man müsste dem MouseListener-Interface eine neue Funktion hinzufügen, etwa &quot;middleButtonDown&quot;. Dann müsste man in jeder Klasse, die von MouseListener erbt, prüfen, ob der code so noch funktioniert (was er im Fall des Klick-Counters nicht tut), was leicht zu bugs führen kann.<br />
Wenn man sich jetzt auch noch vorstellt, dass man das Gleiche mit einem KeyboardListener machen müsste, der Methoden wie &quot;AKeyDown&quot; bereitstellt, läuft es vermutlich jedem von uns kalt den Rücken runter.</p>
<p>Die Lösung ist Vererbung. Man könnte eine Basis-Message für &quot;ButtonDown&quot; - Events erstellen, von der dann alle &quot;...ButtonDown&quot; - Events erben, etwa so:</p>
<pre><code class="language-cpp">struct ButtonDown
{
    unsigned int XPos;
    unsigned int YPos;
};

struct LeftButtonDown
    : public ButtonDown
{
};

struct RightButtonDown
	: public ButtonDown
{
};
</code></pre>
<p>Der Weg vom abstrakten Listener zum eigentlich Handler ist aber alles andere als offensichtlich (zumindest war er das nicht für mich).<br />
Zuerst muss durch virtual Functions jede Message auf die Ebene des Handlers kommen. Danach muss noch irgendwie statt der 1:1 virtual Function calls die normale overload resolution verwendet werden.</p>
<p>Jetzt aber zuerst die Listener - Klasse:</p>
<pre><code class="language-cpp">template&lt; class ...AllMessageTypes &gt;
class Listener;

template&lt;&gt;
class Listener&lt;&gt;
{
private:
    struct MessageDummy;
public:
    void listen( const MessageDummy &amp; );
    //von dieser Klasse wird abgeleitet, um das Listener&lt; ... &gt; - Interface zu benutzen
    template&lt; class HandlerType &gt; //HandlerType ist der Typ der Klasse, die von Handler ableitet
    class Handler;
};

template&lt; class Head , class ...Tail &gt;
class Listener&lt; Head , Tail... &gt;
    : public Listener&lt; Tail... &gt;
{
public:
    virtual void listen( const Head &amp; ) = 0;
    using Listener&lt; Tail... &gt;::listen;
    template&lt; class HandlerType &gt;
    class Handler;
};
</code></pre>
<p>So weit so einfach. Jetzt kommt der schwierigere Teil: Wir müssen zum einen alle rein virtuellen Funktionen überschrieben, andererseits müssen wir immernoch irgendwie Funktionsüberladung verwenden. Ersteres ist wieder mit variadic templates möglich, für den zweiten Teil werden wir SFINAE (-&gt; <a href="http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error" rel="nofollow">http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error</a> ) verwenden.</p>
<p>Zur Verwendung von SFINAE:</p>
<pre><code class="language-cpp">template&lt; class &gt;
class VoidWrapper
{
public:
    typedef void Void;
};

template&lt; class HandlerType , class MessageType &gt;
auto handleIfPossible( HandlerType *Handler , const MessageType *Message ) -&gt; typename VoidWrapper&lt; decltype( Handler-&gt;handle( *Message ) ) &gt;::Void
//Fehler, wenn Handler-&gt;handle( *Message ) nicht möglich ist
{
   Handler-&gt;handle( *Message );
};

template&lt; class HandlerType , class MessageType &gt;
void handleIfPossible( ... )
//schwächer als die andere Version, wegen Ellipse
{
}
</code></pre>
<p>Jetzt kommen wir zur Verwendung von varidadic templates zum Überschreiben der &quot;listen&quot;-Funktionen</p>
<pre><code class="language-cpp">template&lt; class ... &gt;
class TypeList;

template&lt; class HandlerType , class StaticListOfAllMessages , class IterationListOfAllMessages &gt;
class ListenOverwriter;

template&lt; class HandlerType , class ...AllMessageTypes &gt;
class ListenOverwriter&lt; HandlerType , TypeList&lt; AllMessageTypes... &gt; , TypeList&lt;&gt; &gt;
    : public Listener&lt; AllMessageTypes... &gt;
{
protected:
    HandlerType *handler;
};

template&lt; class HandlerType , class SetOfAllMessages , class Head , class ...Tail &gt;
class ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Head , Tail... &gt; &gt;
    : public ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Tail... &gt; &gt;
{
protected:
    using ListenOverwriter&lt; HandlerType , SetOfAllMessages , TypeList&lt; Tail... &gt; &gt;::handler;
    void listen( const Head &amp;Message )
    {
        handleIfPossible&lt; HandlerType , Head &gt;( handler , &amp;Message );
    }
};

//Eigentlich müsste man auch noch für den Fall bei keiner Message Handler definieren. Das lasse ich der Einfachheit halber jetzt weg
template&lt; class HeadMessageType , class ...Tail &gt; template&lt; class HandlerType &gt;
class Listener&lt; HeadMessageType , Tail... &gt;::Handler
	: public ListenOverwriter&lt; HandlerType , TypeList&lt; HeadMessageType , Tail... &gt; , TypeList&lt; HeadMessageType , Tail... &gt; &gt;
{
private:
	using ListenOverwriter&lt; HandlerType , TypeList&lt; HeadMessageType , Tail... &gt; , TypeList&lt; HeadMessageType , Tail... &gt; &gt;::handler;
public:
	Handler( HandlerType &amp;HandlingObject )
	{
		handler = &amp;HandlingObject;
	}
};
</code></pre>
<p>Das wars auch schon!<br />
Verwendet werden kann dieses System nun so (ich verwende wieder die Message von weiter oben):</p>
<pre><code class="language-cpp">typedef Listener&lt; ButtonDown , LeftButtonDown , RightButtonDown &gt; MouseListener;

template&lt; class HandlingType &gt;
class MouseHandler
    : public MouseListener::Handler&lt; HandlingType &gt;
{
public:
    MouseHandler( HandlingType &amp;HandlingObject )
        : MouseListener::Handler&lt; HandlingType &gt;( HandlingObject )
    {
    }
};

class ButtonDownCounter
   : public MouseHandler&lt; ButtonDownCounter &gt;
{
private:
    unsigned int count;
public:
    ButtonDownCounter()
        : MouseHandler&lt; ButtonDownCounter &gt;( *this )
    {
        count = 0;
    }
    void handle( const LeftButtonDown &amp; )
    {
        ++count;
        std::cout&lt;&lt; &quot;LeftButtonDown&quot; &lt;&lt;std::endl;
    }
    void handle( const ButtonDown &amp; )
    {
        ++count;
        std::cout&lt;&lt; &quot;AnyOtherButtonDown&quot;&lt;&lt;std::endl;
    }
};

void sendMessagesTo( MouseListener &amp;ListeningObject )
{
	ListeningObject.listen( LeftButtonDown() );
	ListeningObject.listen( RightButtonDown() );
}

void test()
{
	ButtonDownCounter counter;
	sendMessagesTo( counter );
}
</code></pre>
<p>Dieses Pattern ist also ähnlich einfach zu benutzen wie die Lehrbuch-Variante (naja, vielleicht etwas komplizierter).<br />
Durch diesen message-dispatch Mechanismus sind auch noch andere features denkbar. So ist es etwa möglich, alle Messages an einen Listener zu geben, dessen (polymorpher) Handler diese Messages wiederum an Objekte mit zur Compilezeit veststehende Objekte verteilt. Dadurch kann man mit dem Overhead von nur 1 virtual function call pro Message an beliebig viele Typen verteilen.<br />
Dadurch, dass die Daten der einzelnen Events jetzt in structs stecken, könnte man eine eine message queue bauen, die Messages zwischenspeichert (das ist aber alles andere als trivial).</p>
<p>So, ich hoffe ihr habt bis zum Schluss durchgehalten, ist jetzt doch etwas länger geworden, als ich dachte <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title="=)"
      alt="🙂"
    /><br />
Über feedback und Kritik würde ich mich sehr freuen.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2111837</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2111837</guid><dc:creator><![CDATA[GorbGorb]]></dc:creator><pubDate>Mon, 29 Aug 2011 01:04:12 GMT</pubDate></item><item><title><![CDATA[Reply to c++0x variadic templates - Message&#x2F;Event System on Mon, 29 Aug 2011 04:15:37 GMT]]></title><description><![CDATA[<blockquote>
<p>structs stecken, könnte man eine eine message queue bauen</p>
</blockquote>
<p>Hast du dir die Nachrichtensysteme von SDL, sfml oder Qt schon angesehen? Oder aber das Konzept message passing in Smalltalk? Weiterhin verstehe ich nicht, welches Problem du loesen moechtest oder was deine eigentliche Frage ist.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2111843</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2111843</guid><dc:creator><![CDATA[knivil]]></dc:creator><pubDate>Mon, 29 Aug 2011 04:15:37 GMT</pubDate></item><item><title><![CDATA[Reply to c++0x variadic templates - Message&#x2F;Event System on Mon, 29 Aug 2011 08:28:08 GMT]]></title><description><![CDATA[<p>Wie wäre es mit <code>boost.signals</code> ? Wenn Du das nicht gerade zur Übung machst, dann ist die boost-Sache vermutlich optimal für Dich.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2111899</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2111899</guid><dc:creator><![CDATA[Tachyon]]></dc:creator><pubDate>Mon, 29 Aug 2011 08:28:08 GMT</pubDate></item><item><title><![CDATA[Reply to c++0x variadic templates - Message&#x2F;Event System on Mon, 29 Aug 2011 09:11:36 GMT]]></title><description><![CDATA[<p>Mir ist schon klar, dass es sowas gibt, nur sehe ich nicht wie man damit messages hierarchisch (d.h. eben OOP - mäßig) ordnen kann, ohne mehr Aufwand als einfach eine message von einer anderen abzuleiten.<br />
Des weiteren kann man mit einer ähnlichen Vorgehensweise effektiveren Code schreiben als mit boost::bind und boost::signal.<br />
Dazu folgendes Szenario aus der Spieleentwicklung: Man hat das Spiel in mehrere Subsystems aufgespalten, die jeweils eine Aufgabe haben, etwa &quot;RenderSystem&quot;, &quot;PhysicsSystem&quot; oder &quot;BulletSystem&quot;.<br />
Nun ist es so, dass man immer genau ein solches System hat, das dannn bestimmte Teile der Daten in jedem Tick modifiziert. Natürlich muss man zwischen diesen Systemen kommunizieren, so könnte etwa das PhysicsSystem eine message loslassen, falls ein GameObject mit einem anderen kollidiert ist. Der BulletHandler würde dann prüfen, ob das GameObject eine Bullet ist und dann entsprechend handeln.<br />
Mit boost::signal oder boost::bind müsste sich der BulletHandler nun bei dem PhysicsSystem registrieren, um eine Nachricht zu bekommen. Ich nehme stark an, dass boost::signal sich einfach für jeden boost::connect sich einen Pointer auf das Argument speichert. wird nun ein boost::signal ausgelöst, wird es über alle Pointer iterieren und für jeden eine Art Function Pointer ausführen, d.h. der overhead beim messsage passing nimmt linear mit der Anzahl der Empfänger zu.<br />
Verwendet man meine Herangehensweise, die ich vorgestellt habe, könnte man ein zentrales MessageSystems haben, das am Ende als Handler einen Pointer auf jeden SubSystem - Type bekommt. Wird nun dem Listener des MessageSystems eine message gegeben, wird einmal ein vtable look up stattfinden, dann ist die message beim Handler. Der Handler wird mit SFINAE wie vorgestellt zur compile Zeit feststellen, ob ein SubSystem eine message verwertet. Tut es das wird eine normale Funktion aufgerufen, kein function pointer oder eine virtuelle Function.<br />
Die Komplexität mit boost::signals ist O(n), wobei n die Anzahl der Systems ist, die sich an den Slot connected haben. Ein System wie ich es verwende hat dagegn O(1) (schreibt man das so, wenn es konstant ist?), ist also unabhängig von der Anzahl der sub systems, die ein bestimmtes Event auswerten.<br />
Außerdem kann man mit boost::signals ja wohl sehr schlecht eine generische message queue schreiben kann.<br />
Und zum Schluss kommt noch dazu, dass man eine mit boost::signal eine weitere unnötige Abhängigkeit bekommt: Das BulletSystem muss über das PhysicsSystem Bescheid wissen, um sich dort zu registrieren. Man braucht also ein #include &quot;Physics.hpp&quot; für das BulletSystem. Das braucht man mit meinem message system nicht (sondern nur ein #include &quot;PhysicsMessages.hpp&quot;).<br />
Das kam jetzt vielleicht etwas überheblich rüber, dafür entschuldige ich mich. Mir ist schon klar, dass man mit boost::signals und Konsorten auch sehr gut sowas bauen kann. Ich wollte hier nur mal meinen Ansatz vorstellen, weil ich die Techniken, die dafür verwendet werden, sehr interessant fand.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2111920</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2111920</guid><dc:creator><![CDATA[GorbGorb]]></dc:creator><pubDate>Mon, 29 Aug 2011 09:11:36 GMT</pubDate></item></channel></rss>