Von Method Pointern, Templates und Callback Funktionen



  • Hi!

    Ich schreibe mir zur Zeit ein kleines Eventsystem, das über Callback Methoden läuft. Um die wiederverwendbarkeit zu eröhen habe ich mich dazu entschlossen das ganze über ein Template zu definieren.

    Folgenden Code habe ich "fabriziert":

    template <class _ReceiverClass, typename _CallbackType>
    class CCallback
    {
    private:
    	_ReceiverClass* m_pReceiver;
    	_CallbackType m_pCallback;
    public:
    	CCallback(_ReceiverClass* receiver, _Callbacktype callback);
    	~CCallback(void);
    };
    

    Soweit so gut. Mein nächster Schritt wäre es gewesen eine call-Methode zu implementieren, jedoch stoße ich damit an meine Template Know-How Grenzen. Um eine call Methode zu implementieren müsste ich den Rückgabewert und die Parameterliste von _CallbackType kennen. Diese lassen sich dort jedoch nichtmehr "extrahieren", oder doch?
    Ich gehe davon aus, dass dies nicht möglich ist. Es wäre also notwendig dem Template nicht den typen _CallbackType zu geben, sondern die Typen des Rückgabewertes und der Parameterliste. Ist, und wenn ja, wie, es möglich einem Template eine "Parameterliste" zu übergeben?

    Pseudocode:

    template <class _ReceiverClass, typename _CallbackReturnType, paramlist _CallbackParams>
    class CCallback
    {
    private:
    	_ReceiverClass* m_pReceiver;
    	_CallbackReturnType _ReceiverClass::*(_CallbackParams) m_pCallback;
    public:
    	CCallback(_ReceiverClass* receiver, _Callbacktype callback);
    	_CallbackReturnType call(_CallbackParams);
    	~CCallback(void);
    

    Ich befürchte, dass dies nicht möglich ist. Als alternative schwebt mir zur Zeit vor die Callback Funktion einfach auf einen Parameter zu fixieren. Wenn mehr Parameter benötigt werden kann vom Programmierer ja ein komplexer Parameter, wie ein Pointer auf eine Struktur oder ein Objekt gewählt werden.

    Gibt es da vielleicht irgendwelche Ideen oder Vorschläge, wie ich das ganze alternativ implementieren könnte?

    Grüße,
    Daniel



  • Nein, ist leider im Moment nicht möglich. Wird dann aber mit C++0x möglich sein. Nennt sich variadic templates. Ich würde es somit auch auf 2 Parameter beschränken. Mehr ist normalerweise auch gar nicht nötig.



  • Danke für deine Antwort. Ich habe mal zu implementieren begonnen und bin auf folgende recht eigenartige Fehlermeldung gestoßen, zu welcher ich auch keine Lösung finde.

    error C2659: "=": Funktion als linker Operand.

    (MSVC++ 2008)

    Hier die relevanten Code Abschnitte:

    template <class _ReceiverClass, typename _CallbackReturnType, typename _CallbackParamType>
    class CCallback
    {
    public:
    	typedef _CallbackReturnType _ReceiverClass::*CallbackType(_CallbackParamType);
    
    // ... 
    
    	CallbackType m_pCallback;
    
    // ... 
    
    inline CCallback(_ReceiverClass* receiver, CallbackType callback) : m_pReceiver(receiver)
    	{
    		m_pCallback = callback;
    	}
    

    Ich verstehe nicht weshalb dieser Fehler auftritt und wie ich ihn besieitgen kann. Ich nehme an, dass er in Zusammenhang mit den Templates steht, aber ich bin mir nicht sicher, denn *eigentlich* sollte die Zuweisung ok sein.

    Grüße,
    Daniel



  • Habe die Lösung zu meinem Problem finden können, ich habe bei der Definition des Method Pointer Typs einen kleinen Fehler gemacht und zwei Klammern vergessen:

    public:
        typedef _CallbackReturnType (_ReceiverClass::*CallbackType)(_CallbackParamType);
    

    So klappt es.

    Grüße,
    Daniel



  • Es hat sich bezüglich meiner Callback Implementation eine weitere Frage aufgetan. Ich möchte nun in einem Objekt eine Liste von registrierten Callback Methoden sichern. Ich hatte eigentlich vor dazu den STL set Container zu nutzen, damit Callback Funktionen nicht doppelt auftreten können.

    Da der Container die Implmentation des operator< (oder eines speziellen Sortierobjektes) erwartet habe ich meine CCallback Klasse um diesen Operator erweitern wollen. Leider lässt sich auf einen Methoden Pointer wohl keine klassische Zeigerarithmetic anwenden - somit auch kein Kleins-Als vergleich. Folgender Code lässt sich nicht kompilieren:

    //CCallback::operator<(CCallback& op)
    {
        return (this->m_pCallback < op.m_pCallback) || (this->m_pReceiver < op.m_pReceiver)
    }
    

    Wie ich gelesen habe lassen sich Pointer auf Methoden auch nicht auf untypisierte Datenzeiger (void*) casten - und wohl auch nicht auf andere ordinale Typen. Wie kann ich nun also meinen STL Container befriedigen und eine operator< Methode implementieren, wenn es keine Möglichkeit gibt meinen Methodenpointer einzuordnen?

    Grüße,
    Daniel



  • Dann nimm doch einen Container, der den < - Operator nicht verlangt..



  • Ich hätte zwar gerne std::set benutzt, aber, wenn es keine andere Möglichkeit gibt, muss ich wohl auf einen anderen Container zurück greifen und die Einzigartigkeit der Elemente selbst überprüfen.

    Ich habe nun list genommen und habe alles zum Laufen bekommen. Vielen Dank!

    Callback.h

    Grüße,
    Daniel



  • Du kannst statt operator< auch einen Funktor oder eine Funktion schreiben, welche(r) die Elemente überprüft und als Sortierkriterium an std::set übergeben werden kann.

    Denn wenn du in einem sequenziellen Container auf die Einzigartigkeit der Elemente prüfen kannst, sollte es auch möglich sein, das Kriterium vorher festzulegen und damit auch assoziative Container ermöglichen.



  • Was genau meinst du mit "das Kriterium vorher festlegen"?
    Auf Methoden Pointer lässt sich der == operator anwenden, aber keine ordinalen Operatoren (<, >, <=, >=). Ich habe schon mit dem Gedanken gespielt eine Funktion zu schreiben, die in etwa so aussieht:

    Pseudocode

    bool comp(CCallbackClass& op1, CCallbackClass& op2)
    {
       if (op1.methodpointer != op2.methodpointer)
       {
          return op1.instancepointer < op2.instancepointer
       }
       else
          return false;
    }
    

    Allerdings dürfte eine solche Funktion wohl jegliche Strukturen, die die STL zum einordnen der Elemente in einem Set nutzen könnte entarten, wenn nicht vielleicht sogar zerstören (ein Binärer Baum würde ganz sicher entarten, ich bin mir nicht ganz sicher wie andere, kompliziertere Strukturen sich verhalten würden) und damit den Sinn des Containers verfehlen.

    Grüße,
    Daniel



  • Sorry, ich hatte dich falsch verstanden. Ich dachte, du würdest dann nachträglich die einzelnen Objekte manuell vergleichen. Dabei habe ich übersehen, dass du dann nur auf Gleichheit prüfst.

    In dem Fall ist dein jetziger Ansatz besser. Zudem sind ordinale Vergleiche von Zeigern (auch Instanzzeiger) nicht immer definiert.


Anmelden zum Antworten