2010-11-24 12 views
2

Ich betrete einen Bereich, der neu für mich ist, aber im Grunde muss ich Callbacks in C++ implementieren. Ich entwerfe ein Toolkit, mit dem ich mein Leben vereinfachen kann. Im Grunde ist es ein .dll-Plugin, das meinen anderen .dll-Plugins viele Funktionen bietet.C++ Rückrufe? Sollte ich Member Func Pointer/Delegierte/Events benutzen?

Eine dieser Funktionen ist HookEvent (const char * Ereignisname, void * Callback), die es mir ermöglichen, verschiedene Ereignisse, die ausgelöst werden, haken. Hier wäre ein Beispiel ...

Example_Plugin1.dll tut HookEvent ("player_spawn", & Plugin1 :: Event_PlayerSpawn); Example_Plugin2.dll tut HookEvent ("player_spawn", & Plugin2 :: Event_PlayerSpawn);

Ich muss herausfinden, die beste (und vorzugsweise einfachste) Methode der Einrichtung eines Callback-System, das dafür gut funktioniert. Ich habe jetzt ein paar Stunden C++ Callbacks gelesen und viele verschiedene Ansätze gefunden.

Ich nehme an, die einfachste Sache zu tun wäre, eine Vorlage zu erstellen, und verwenden Sie typedef bool (ClassName :: * EventHookCallback) (IGameEvent, Bool); Danach bin ich ein bisschen neblig.

Ich habe auch gelesen, dass Delegaten oder ein .NET-Stil-Event-System andere mögliche Ansätze sind. Ich bin schon etwas verwirrt, also möchte ich mich nicht mehr verwirren, denke aber, dass es sich lohnt, danach zu fragen.

Hier ist ein Link zu dem C++ .NET Stil-Ereignissystem, über das ich gelesen habe. http://cratonica.wordpress.com/2010/02/19/implementing-c-net-events-in-c/

Also was schlagen Sie vor? Irgendwelche Tipps, so weit wie es zu implementieren wäre am meisten geschätzt.

Antwort

3

Wenn Sie eine generalisierte Ereignisauslösung wünschen, könnte Boost.Signals2 anwendbar sein.

Boost.Signals2 Die Bibliothek ist eine Implementierung eines verwalteten Signale und Schlitze System. Signale repräsentieren Callbacks mit mehreren Zielen, und werden in ähnlichen Systemen auch Herausgeber oder Ereignisse genannt. Signale sind verbunden mit einer Reihe von Steckplätzen, die sind Callback-Empfänger (auch Ereignisziele oder Abonnenten genannt), die aufgerufen werden, wenn das Signal "emittiert wird."

Auch wenn Sie dieses Maß an Flexibilität nicht benötigen, sollten Sie in der Lage sein, die Funktion Bindung in Ihrem Code mit Boost.Bind oder der C++ 0x-Äquivalente zu vereinfachen.

EDIT: Es gibt eine ausgezeichnete Diskussion von Herb Sutter über die Probleme, denen Sie begegnen könnten here. Sie können dies als Anleitung verwenden, wenn Sie sich entscheiden, dass Sie nicht den vollständigen Boost-Funktionssatz benötigen, und rollen Sie Ihren eigenen.

+0

Ich hoffte, es zu tun, ohne Boost oder eine andere zusätzliche Bibliothek zu verwenden, wenn möglich. Wäre das wirklich so viel einfacher als nur mit Mitgliedsfunktionszeigern oder Delegaten? –

+1

Wenn Sie einfache gelegentliche Rückrufe benötigen, dann ist das Overkill. Für eine umfassendere Observer-Implementierung wird dies hilfreich sein.Die Kosten/Nutzen-Entscheidung hängt von Ihren Erwartungen ab. Wenn Sie in Ihrem Compiler C++ 0x haben, können Sie dem Delegaten sogar ein Lambda (lokale anonyme Funktion) zuweisen. Überprüfen Sie dies: http://uint32t.blogspot.com/2009/05/using-c0x-lambda-to-replace-boost-bind.html –

+0

Ich mag das eigentlich, aber ich denke, ich werde nur damit gehen System, wie es mir von allem, was ich bisher gesehen habe, am verständlichsten erscheint. http://cratona.wordpress.com/2010/02/19/implementing-c-net-events-in-c/ –

1

Wie wäre es mit Qt Signal und Slot? Es tut, was Rückrufe tun, aber ohne die Unordnung, etwas zu machen, das nicht Teil Ihrer Rückrufparameter global ist.

0

Klingt, als ob Sie interessiert sein könnten, wie Sie Ihr eigenes Plugin-Framework erstellen. Die Probleme, denen Sie begegnen werden, sind wahrscheinlich dieselben. Werfen Sie einen Blick auf diesen schönen Dr. Dobbs Artikel Building Your Own Plugin Framework.

Hoffe, das hilft!

+0

So interessant das aussieht, ich glaube, dass das für meine Bedürfnisse extrem kompliziert ist. Wie ich bereits erwähnt habe, benutze ich bereits ein .dll-Plugin, um diese Funktionen zu meinen anderen Plugins zu machen/verfügbar zu machen, und die API, die ich verwende, bietet einfache Methoden dafür. –

+0

Klingt, als wüsstest du, was du brauchst. Viel Glück! –

1

Boost.Signals wäre meine Wahl, kombiniert mit Dingen wie boost::bind und Boost.Function.

0

Ich würde eine abstrakte Basisklasse als Plugin-Schnittstelle verwenden. (Und in der Tat habe ich ein Muster wie das unten vor verwendet.)

Bibliothek, PluginIfc.h:

class PluginIfc { 
public: 
    virtual ~PluginIfc() = 0; 
    virtual bool EventCallback(const char* event_name, IGameEvent, bool) = 0;  
}; 

// For Windows, add dllexport/dllimport magic to this declaration. 
// This is the only symbol you will look up from the plugin and invoke. 
extern "C" PluginIfc* GetPlugin(); 

Plugin:

#include <PluginIfc.h> 
class Plugin1 : public PluginIfc { 
public: 
    virtual bool EventCallback(const char* event_name, IGameEvent, bool); 
    Plugin1& get() { return the_plugin_obj; } 

    bool Event_PlayerSpawn(IGameEvent, bool); 
    // ... 
private: 
    std::vector<std::string> _some_member; 

    static Plugin1 the_plugin_obj; // constructed when plugin loaded 
}; 

Plugin1 Plugin1::the_plugin_obj; 
PluginIfc* GetPlugin() { return &Plugin1::get(); } 

Auf diese Weise Ihre Klassen-Plugin kann haben Sie einfach Mitglieder, und C++ 's virtueller Aufruf Mechanismus kümmert sich darum, Ihnen einen guten this Zeiger in EventCallback zu geben.

Es könnte verlockend sein, eine virtuelle Methode pro Ereignistyp zu machen, sagen wir einfach Event_PlayerSpawn und ähnliche Methoden virtuell. Wenn Sie jedoch einen Ereignistyp hinzufügen möchten, bedeutet das, dass Sie die Klasse PluginIfc ändern müssen. Ihre alten kompilierten Plugins sind nicht mehr kompatibel. Daher ist es sicherer, eine String-Ereigniskennung (für die Erweiterbarkeit) zu verwenden und die wichtigsten Sortierereignisse für Callbacks auf spezifischere Methoden zu beschränken.

Der Hauptnachteil hier (im Vergleich zu einer Signal-Slot-Typ-Implementierung) ist, dass alle Rückrufe müssen die gleiche Reihe von Argumenten. Aber deine Frage klang so, als wäre das angemessen. Und es ist oft möglich, innerhalb dieser Einschränkung zu arbeiten, indem Sie sicherstellen, dass der Satz von Argumenten sehr flexibel ist und Strings verwendet, die geparst werden sollen, oder Any -Style-Objekte.

0

Die Implementierung eines eigenen Rückrufsystems ist nicht trivial.
Mein Ziel ist es, Ereignistypen spezifischen Rückruffunktionen zuzuordnen.
Zum Beispiel Wenn das "player_spawn" -Ereignis angestiegen ist, wird das & Plugin1 :: Event_PlayerSpawn aufgerufen.
Also, was Sie tun sollten, ist folgendes:
1) Definieren Sie alle Ereignisse von Interesse. Mache sie so allgemein wie möglich. Sie können alle Informationen einkapseln, die Sie benötigen
2) Erstellen Sie einen Registrar. I.e. eine Klasse, in der alle Module ihr Interesse für bestimmte Methoden der -Klasse registrieren. Z.B. Registrar.register(player_spawn,this,Event_PlayerSpawn);
3) Registrar hat eine Warteschlange aller Abonnenten.
4) Sie können auch eine einheitliche Schnittstelle für die Module haben. I.e. alle Modul eine bestimmte Funktion implementieren, sondern Daten auf Basis Veranstaltung verschiedene Dinge
5) tun können, wenn ein Ereignis eintritt, werden alle Teilnehmer für die jeweilige Veranstaltung interessiert benachrichtigt, die entsprechende Funktion
6) Teilnehmer anrufen kann de -Registrieren, wann immer es nötig ist
Hoffe, das hilft.