2010-11-26 9 views
1

Ich folge Lars Haendels Functor tutorial auf newty.de, um ein Callback-System einzurichten. Ich bin jedoch ein wenig verwirrt und ich hoffe, dass jemand mir helfen kann.C++ Functor Callback Setup

Hier ist meine Functor Vorlage

#include <igameevents.h> 

// Abstract Base Class (Functor) 
class TBaseCallback 
{ 
public: 

    // two possible functions to call member function. virtual cause derived 
    // classes will use a pointer to an object and a pointer to a member function 
    // to make the function call 
    virtual void operator()(IGameEvent *pEvent){}; // call using operator 
    virtual void Call(IGameEvent *pEvent) {};  // call using function 
}; 


// Derived Template Class 
template <class TClass> class TEventCallback : public TBaseCallback 
{ 
private: 

    void (TClass::*funcPtr)(IGameEvent*);  // pointer to member function 
    TClass* thisPtr;       // pointer to object 

public: 

    // constructor - takes pointer to an object and pointer to a member and stores them in two private variables 
    TEventCallback(TClass* _thisPtr, void(TClass::*_funcPtr)(const char*)) 
    { thisPtr = _thisPtr; funcPtr=_funcPtr; }; 

    // override operator "()" 
    virtual void operator()(IGameEvent *pEvent) 
    { (*thisPtr.*funcPtr)(pEvent); };   // execute member function 

    // override function "Call" 
    virtual void Call(IGameEvent *pEvent) 
    { (*thisPtr.*funcPtr)(pEvent); };   // execute member function 
}; 

Was ich tun möchte, ist im Grunde andere .dlls erlauben meine HookGameEvent() Funktion zu nutzen, und wenn ein Spielereignis aufgerufen wird, kann ich durch einen Vektor laufen | | Liste meiner Hooks, überprüfen Sie, ob der Name des Ereignisses übereinstimmt, und führen Sie die Callbacks nach Bedarf aus. Was mich jedoch verwirrt, ist, wie ich den Callback in meiner HookEvent-Struktur speichern kann, die so aussieht.

Ich habe es für jetzt auskommentiert, aber ich bin sicher, es ist offensichtlich, was ich verwirrt bin und wo ich vermasseln. Wenn jemand Hilfe leisten kann, wäre das sehr willkommen.

+0

Ich konnte nicht anders, als festzustellen, dass Ihr Funktionszeiger in der TEventCallback-Klasse mit einem Parameter (const char *) definiert ist, aber Sie rufen ihn mit einem (IGameEvent *) im Code auf. – Daemin

+0

Das zeigt den Wert von typedefing-Funktionstypen. @Brett: Wenn Sie mit Fehlermeldungen zu tun haben, wäre es für uns sehr hilfreich, wenn Sie sie in Ihre Frage aufnehmen würden. Wenn der Code aus anderen Gründen nicht erreicht, was Sie wollen, wäre es auch hilfreich, wenn Sie uns diese mitteilen. Denken Sie daran, erzählen Sie uns, was Sie wollen und was tatsächlich passiert, sonst raten wir nur. – outis

+0

Das Problem, das ich hatte, war, wie man es in die Struktur einfügt, das (const char *) war nur übrig von, als ich dieses Tutorial benutzte. –

Antwort

3

Die meisten Leute verstehen Vererbung nicht. Im Allgemeinen sind abgeleitete Klassen Implementierungsdetails. Das einzige Mal, wenn du ihre Namen aussprichst, sind sie zu konstruieren. Darüber hinaus sollten virtuelle Funktionen in einer Basis privat und rein sein und in abgeleiteten Klassen vollständig unzugänglich sein. Es ist ein Design-Fehler in C++, dass dies nicht erzwungen wird.

 
struct TBaseCallback 
    void operator()(IGameEvent *pEvent) { _Call(pEvent); }; 
    void Exec(IGameEvent *pEvent) { _Call(PEvent); } 
private: 
    virtual void _Call(IGameEvent *pEvent)=0; 
}; 

struct EventHook 
{ 
    char *name; 
    EventHookMode mode; 
    TBaseCallback *p; 
    void dispatch(char *msg; IGameEvent *e) const { 
     if(strcmp(msg,name)==0) p->Exec(e); 
    } 
}; 

Mit diesem Design macht es keinen Unterschied, was in Klassen von TBaseCallback abgeleitet ist, und sollte es auch nicht. Nur die Abstraktion sollte jemals öffentlich sichtbar sein. Im normalen Code ist das schwer zu erzwingen.Wenn Sie DLLs zum Abrufen der abgeleiteten Klassen verwenden, ist dies zwingend erforderlich, da der Satz abgeleiteter Klassen offen/beliebig/unendlich/unbestimmt ist (wählen Sie).

Übrigens: Wenn Sie dies auf komplexere Abstraktionen anwenden, werden Sie bald herausfinden, warum die Objektorientierung ein gebrochenes Konzept ist. Mit DLL geladen abgeleiteten Klassen, können Sie einfach mit dynamic_cast-Schalter betrügen (weil sie geschlossen/spezifisch/endlich/bestimmt sind).

0

Die Klasse, die die Rückrufe ausführen soll, sollte eine Liste der Functor-Objekte enthalten, die aufgerufen werden sollen. Diese würden Ihre

std::vector<EventHook*> m_EventHooks; 

Nun sollte die eventhook haben eine virtuelle Funktion:

struct EventHook 
{ 
    ... 
    virtual void notifyMe(); 
} 

Dann jeder, der interessiert ist, benachrichtigt zu werden wird seine eigene Implementierung des Hakens erstellen:

struct MyEventHook : public EventHook 
{ 
    virtual void notifyMe() { ... whatever I want to do in that case ... } 
} 

Durch die Wunder des Polymorphismus, wenn Sie dann über alle Elemente Ihres m_EventHooks-Containers iterieren und notifyMe() für diese aufrufen, wird die korrekte Klassenversion cal sein LED.

0

Das Problem, das ich sehe (und es könnte sehr gut andere sein) ist, dass in pEventCallback 's-Typ der Template-Parameter sollte ein Klassentyp sein, ist aber eigentlich ein Zeigertyp. Ein Fix (ohne Begrenzung, welche Arten die Callback-Wraps) ist der Basistyp zu verwenden:

struct EventHook 
{ 
    char *name; 
    EventHookMode mode; 
    TBaseCallback* pCallback; 
}; 

Wenn es mehr zu TEventCallback ‚s API ist, und es muss zugänglich sein durch eine EventHook, sollten Sie den Code bewegen TEventCallback, die ein Objekt und seine Methode in einer separaten Unterklasse behandelt.

// Example EventCallback that takes other args 
class EventCallback : public TBaseCallback { 
public: 
    EventCallback(); 
    EventCallback(const EventArgs& evtArgs); 
    // EventCallback specific methods ... 
    virtual EventArgs& args(); 
    virtual const EventArgs& args() const; 
} 

/* TReturn allows for calling methods with a non-void return. Return value is ignored. 
*/ 
template <class TClass, typename TReturn = void> 
class TMethodCallback : public EventCallback 
{ 
private: 
    typedef TReturn (TClass::*TMeth)(IGameEvent*); 
    TMeth funcPtr;  // pointer to member function 
    TClass* thisPtr;       // pointer to object 

public: 

    // constructor - takes pointer to an object and pointer to a member and stores them in two private variables 
    TMethodCallback(TClass* _thisPtr, TMeth _funcPtr) 
    { thisPtr = _thisPtr; funcPtr=_funcPtr; }; 

    // override operator "()" 
    virtual void operator()(IGameEvent *pEvent) 
    { (*thisPtr.*funcPtr)(pEvent); };   // execute member function 

    // override function "Call" 
    virtual void Call(IGameEvent *pEvent) 
    { (*thisPtr.*funcPtr)(pEvent); };   // execute member function 
}; 

Off-Topic

Genauso gut könnte man die TBaseCallback::operator() Standardimplementierung von TBaseCallback::Call Anruf.

void TBaseCallback::Call(IGameEvent *pEvent) { this->operator()(pEvent); }; 
+0

Vielen Dank, dies scheint alle meine Probleme zu decken. Eine Frage ist jedoch, im Grunde, wenn ein anderes Plugin meins verwendet, um einen Haken zu erstellen, hier ist, wie es aussieht EventHookError HookEvent (const Char * Name, void * thisptr, void * Rückruf, EventHookMode-Modus = EventHookMode_Post); Wenn der Callback in der Struktur ein TBaseCallback ist, dann wird es nicht die Klasse/funcPtr genommen, so dass ich den Callback nicht wirklich speichern kann, werde ich das tun? –

+0

Der API-Client wäre für das Erstellen des Rückrufobjekts zuständig. Der Prototyp für 'HookEvent' wäre' EventHookError HookEvent (const char * Name, TBaseCallback * pCallback, EventHookMode mode = EventHookMode_Post) '. – outis

0

Ich denke, dass Sie einen komplizierten Compilerfehler bekommen werden, weil Sie T * anstelle von T in Ihrer Vorlageninstanziierung verwenden.

Try this:

struct EventHook 
{ 
    char *name; 
    EventHookMode mode; 
    TEventCallback<IGameEvent> pEventCallback; 
}; 

sollte kompilieren, wenn das, was Sie wollen.