2017-10-11 2 views
3

Ich mag würde meine Klassenmethode als Argument für eine (Fremd) Funktion (listner -, die nicht geändert werden kann) zu übergeben, die einen Funktionszeiger und eine void* akzeptiert . Es folgt ein Beispiel:Bind Klassenmethode und geben sie als Funktionszeiger

#include <functional> 

typedef void(*pfnc) (void*); 

struct Foo 
{ 
    static void static_foo(void*) 
    { 
    } 

    void foo(void*) 
    { 
    } 

    void listner(pfnc f, void* p) 
    { 
     f(p); 
    } 

    void test() 
    { 
     listner(static_foo); // works with static method 

     auto f = [](void*) {}; 
     listner(f); // works with lambda 

     std::function<void(void*)> stdf = std::bind(&Foo::foo, this, std::placeholders::_1); 
     listner(stdf); // does not compile with not static method 
    } 
}; 

Leider kompiliert meine Lösung nicht. Was muss ich ändern?

+1

Funktionszeiger haben keinen Status. Sie müssen den 'void *' Parameter verwenden. Auf der positiven Seite existiert der Parameter, im Gegensatz zu einigen anderen spezifischen C APIs, die mir begegnet sind. – chris

+1

Sind Sie sicher, dass die 'listener' Methode nur' pfnc' akzeptiert? Normalerweise akzeptieren solche Methoden einen vom Benutzer angegebenen 'void *' - Parameter, der während des Callbacks an die Handler-Methode übergeben wird. –

+0

Normalerweise verwende ich ein Lambda, das dies erfasst, um die Methode aufzurufen. –

Antwort

1

Aus der Sicht des Callback-Signals nimmt die Listener-API einen Zeiger auf void als 'benutzerdefinierte Daten'. Sie können auf Foothis als die Daten und eine kleine staatenlos Proxy-Funktion auf dem Weg zum Handler übergeben:

typedef void(*pfnc) (void*); 

struct Foo 
{ 
    static void static_foo(void*) 
    { 
    } 

    void foo() 
    { 
    } 

    void listner(pfnc f, void* user_data) 
    { 

     // eventually calls 
     f(user_data); 
    } 

    void test() 
    { 
     listner(static_foo, nullptr); // works with static method 

     auto f = [](void*) { 
     }; 
     listner(f, nullptr); // works with lambda 

     listner([](void* pv) 
     { 
      reinterpret_cast<Foo*>(pv)->foo(); 
     }, this); 
    } 

}; 
0

Für das Beispiel in der Frage es möglich wäre, die Mitglied Funktionszeiger zu übergeben. Aber natürlich muss die Instanz bekannt sein, zu der das Mitglied aufgerufen werden soll. Wenn die aufrufende Funktion auch eine Mitgliedsfunktion ist, können wir this verwenden, um die Mitgliedsfunktion aufzurufen, die über den Zeiger übergeben wird.

struct Foo 
{ 
    static void static_foo(void*) 
    { 
    } 

    void foo(void*) 
    { 
    } 

    void listner(pfnc f) 
    { 
    } 

    void listner(void(Foo::*f)(void*)) 
    { 
     (this->*f)(nullptr); // call member function pointer for this 
    } 

    void test() 
    { 
     listner(static_foo); // works with static method 

     auto f = [](void*) {}; 
     listner(f); // works with lambda 

     listner(&Foo::foo); // pass pointer to member function 
    } 
}; 
0

Ihr Beispiel mit einer statischen Member-Funktion ‚funktioniert‘, weil diese auf einfache alte Funktionen fast gleichwertig ist

Es auch ‚Arbeiten‘ mit dem Lambda Sie liefern, weil es ein Capture-weniger Lambda welches in einen Funktionszeiger umgewandelt werden kann (vgl. Passing lambda as function pointer) Es funktioniert jedoch nicht mit einem Lambda mit einem Capture, weil, wie chris gesagt wird, Funktionszeiger keinen Zustand haben, dh es gibt keinen Platz zum Speichern, was erfasst würde .

Das gleiche gilt für die Bindung. Sie können es in einer std :: -Funktion speichern, da ein solches Objekt zwar einen Status hat, aber nicht in einem einfachen (statuslosen) Funktionszeiger gespeichert werden kann.

Wenn Sie nicht überzeugt sind, dann haben wir einen Blick auf stdf.target_type() Sie erzählt und vergleichen Sie es mit typeid(static_foo).name() Sie können auch einen Blick auf die Rückgabetyp std haben :: bind

Am Ende als andere Haben Sie gesagt, Sie müssen Ihr Objekt in den Benutzerdaten, dh in der void * einschiffen. Es könnte sich lohnen zu untersuchen, ob Sie das in eine Hilfsfunktion umwandeln können, indem Sie eine std :: -Funktion als Parameter verwenden ...

Verwandte Themen