2010-01-27 7 views
9

Ich habe eine Timer-Klasse erstellt, die eine Rückrufmethode aufrufen muss, wenn der Timer abgelaufen ist. Zur Zeit arbeite ich mit normalen Funktionszeigern (sie werden als void (*) (void) deklariert, wenn das Ereignis Elapsed auftritt, wird der Funktionszeiger aufgerufen.So definieren Sie einen allgemeinen Mitgliedsfunktionszeiger

Es ist möglich, dasselbe mit einer Mitgliedsfunktion zu tun, die das hat auch die Signatur void (AnyClass :: *) (void)

Dank mates

EDIT:?. Dieser Code hat unter Windows und auch auf einem Echtzeit-Betriebssystem (VxWorks) so nicht mit externen Bibliotheken arbeitet wäre großartig

EDIT2: Nur um sicher zu sein, was ich brauche, ist eine Timer-Klasse, die ein Argument am Konstruktor von nehmen tipe "AnyClass.AnyMethod" ohne Argumente und Rückgabe void. Ich muss dieses Argument und das Letztere in einem Punkt des Codes speichern und nur die Methode ausführen, auf die diese Variable zeigt. Die Hoffnung ist klar.

+0

Sind Sie sicher, dass der Funktionszeiger nicht als 'void (*) (void *)' deklariert ist. Ich wäre sehr schockiert, wenn es keine "void *" als Parameter akzeptieren würde. –

Antwort

8

Abhängigkeiten, Abhängigkeiten ... ja, sicher Boost ist nett, so ist mem_fn, aber Sie brauchen sie nicht. Allerdings ist die Syntax-Elementfunktionen aufrufen übel, so ein wenig Vorlage Magie hilft:

class Callback 
    { 
    public: 
     void operator()() { call(); }; 
     virtual void call() = 0; 
    }; 

    class BasicCallback : public Callback 
    { 
     // pointer to member function 
     void (*function)(void); 
    public: 
     BasicCallback(void(*_function)(void)) 
      : function(_function) { }; 
     virtual void call() 
     { 
      (*function)(); 
     }; 
    }; 

    template <class AnyClass> 
    class ClassCallback : public Callback 
    { 
     // pointer to member function 
     void (AnyClass::*function)(void); 
     // pointer to object 
     AnyClass* object;   
    public: 
     ClassCallback(AnyClass* _object, void(AnyClass::*_function)(void)) 
      : object(_object), function(_function) { }; 
     virtual void call() 
     { 
      (*object.*function)(); 
     }; 
    }; 

Jetzt können Sie nur Rückruf verwenden als Rückrufspeichermechanismus so:

void set_callback(Callback* callback); 
set_callback(new ClassCallback<MyClass>(my_class, &MyClass::timer)); 

Und

Callback* callback = new ClassCallback<MyClass>(my_class, &MyClass::timer)); 

(*callback)(); 
// or... 
callback->call(); 
+0

Ok, bedenke, dass ich hier fast nichts verstehe. C++ ist nicht das, mit dem ich arbeite, ich muss nur ein paar Änderungen an einem laufenden Projekt vornehmen, also erwarte ich, dass ich all diese Magie nicht verstehe. Wie kann ich den Rückruf ausführen? Wenn ich nur callback(); Ich bekomme einen Begriff nicht zu einer Funktion mit 0 Argumente ausgewertet. Vielen Dank im Voraus. –

+0

Und die Klasse BasicCallback sieht so aus, wird nirgendwo benutzt. Vielleicht ist da ein Fehler? –

+0

@SoMoS, BasicCallback ermöglicht es Ihnen, dieselbe Schnittstelle mit Ihren alten Callbacks zu verwenden - es wird keine Klasse benötigt. Sie haben Ihren Fehler erhalten, weil Sie versucht haben, einen Zeiger "auszuführen", "(* callback)()" wäre der richtige Aufruf. Ich habe meinen Code ein wenig modifiziert und eine explizite Call-Methode hinzugefügt, vielleicht wird das klarer. –

4

Die beste Lösung, die ich für den gleichen Zweck verwendet habe, waren boost::signal oder boost::function Bibliotheken (je nachdem, ob Sie einen einzelnen Rückruf wollten oder viele von ihnen) und boost::bind, um tatsächlich die Rückrufe zu registrieren.

class X { 
public: 
    void callback() {} 
    void with_parameter(std::string const & x) {} 
}; 
int main() 
{ 
    X x1, x2; 
    boost::function< void() > callback1; 

    callback1 = boost::bind(&X::callback, &x1); 
    callback1(); // will call x1.callback() 

    boost::signal< void() > multiple_callbacks; 
    multiple_callbacks.connect(boost::bind(&X::callback, &x1)); 
    multiple_callbacks.connect(boost::bind(&X::callback, &x2)); 
    // even inject parameters: 
    multiple_callbacks.connect(boost::bind(&X::with_parameter, &x1, "Hi")); 

    multiple_callbacks(); // will call x1.callback(), x2.callback and x1.with_parameter("Hi") in turn 
} 
+1

Aber ist eine externe Bibliothek nicht boost? – futureelite7

+4

Ja, sie sind nicht Teil des Standards, aber Boost ist die Standard-Nicht-Standard-Bibliothek, die Sie finden werden. –

+1

Außerdem werden 'function' und' bind' zur C++ 0x Standardbibliothek hinzugefügt (möglicherweise bereits im 'std :: tr1' Namespace verfügbar). – UncleBens

1

boost::function sieht hier wie eine perfekte Passform aus.

+0

Er sagte keine externen Bibliotheken. –

+1

Oh, verpasste das :) Bitte beachten Sie, dass boost :: function nur eine Header-Bibliothek ist, also keine Link-Abhängigkeiten. –

2

Vielleicht ist der Standard bereits gut genug für das, was Sie wollen. Es ist Teil von STL.

+0

Lassen Sie mich es überprüfen. Sieht gut aus. –

+0

Ich schlug boost :: function vor, da ich annahm, dass der Timer eine Klasse ohne Template sein würde, die den Callback für die spätere Verwendung behalten musste. Da mem_fun als Vorlage dient, tackern Sie entweder die Timer-Klasse in der Klasse des Callbacks, oder Sie müssen mem_fun in eine Klasse ohne Template einbetten, und hier implementieren Sie boost :: function selbst ... –

0

ich bin eine Schnittstelle wie diese Annahme:

void Timer::register_callback(void(*callback)(void*user_data), void* user_data); 

template<typename AnyClass, (AnyClass::*Func_Value)(void)> 
void wrap_method_callback(void* class_pointer) 
{ 
    AnyClass*const self = reinterpret_cast<AnyClass*>(class_pointer); 
    (self->*Func_Value)(); 
} 

class A 
{ 
public: 
    void callback() 
    { std::cout << m_i << std::endl; } 
    int m_i; 
}; 

int main() 
{ 
    Timer t; 
    A a = { 10 }; 
    t.register_callback(&wrap_method_callback<A,&A::callback>, &a); 
} 

Ich denke, eine bessere Lösung wäre, Callback Callback auf boost :: function oder eine selbst erstellte Version (wie Kornels Antwort) zu aktualisieren. Dies erfordert jedoch echte C++ - Entwickler, um sich zu beteiligen, andernfalls werden Sie sehr wahrscheinlich Fehler einführen.

Der Vorteil meiner Lösung ist, dass es nur eine Template-Funktion ist. Nicht viel kann schief gehen. Ein Nachteil meiner Lösung ist, dass es Ihre Klasse mit Cast zu void* und zurück schneiden kann. Achten Sie darauf, dass nur AnyClass* Zeiger als void* an die Rückrufregistrierung übergeben werden.