2009-06-16 8 views
34

Ich habe eine C-Bibliothek, die eine Callback-Funktion benötigt, um für die Anpassung einer Verarbeitung registriert zu werden. Der Typ der Callback-Funktion ist int a(int *, int *).Verwenden einer C++ - Klassenmitgliedsfunktion als C-Callback-Funktion

Ich schreibe C++ Code ähnlich die folgenden und versuchen, ein C++ Klasse Funktion als Callback-Funktion zu registrieren:

class A { 
    public: 
    A(); 
    ~A(); 
    int e(int *k, int *j); 
}; 

A::A() 
{ 
    register_with_library(e) 
} 

int 
A::e(int *k, int *e) 
{ 
    return 0; 
} 

A::~A() 
{ 

} 

Der Compiler führt folgende Fehlermeldung:

In constructor 'A::A()', 
error: 
argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’. 

Meine Fragen:

  1. Zunächst ist es möglich, eine C++ - Klasse Memeber-Funktion wie ich versuche zu registrieren, und wenn ja, h ow? (Ich lese 32,8 bei http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Aber meiner Meinung nach löst es das Problem nicht)
  2. Gibt es eine alternative/bessere Möglichkeit, dies anzugehen?

Antwort

37

Sie können das tun, wenn die Elementfunktion statisch ist.

Nicht statische Elementfunktionen der Klasse A haben einen impliziten ersten Parameter vom Typ class A*, der diesem Zeiger entspricht. Deshalb konnten Sie sie nur registrieren, wenn die Signatur des Callbacks auch den ersten Parameter class A* hatte.

+0

ja. Diese Lösung hat funktioniert. Was mich verwirrt, ist Compiler nicht Fehler gezeigt int (A ::) (A *, int *, int *) 'stimmt nicht' int() (int, int *) ' – Methos

+0

Es tat, aber indem (A: :) was bedeutet, dass die Funktion Teil der Klasse A ist, was von dort den 'this' Zeiger impliziert. – GManNickG

+0

Ich bin nur neugierig ... ist das im Standard angegeben? Ich schaute nur auf den Abschnitt über Klassen und fand das nicht. Trotzdem sehr interessant. Ich würde einfach nicht denken, dass jeder Compiler notwendigerweise nicht-statische Elementfunktionen auf diese Weise behandeln muss. – Tom

1

Das Problem bei der Verwendung einer Elementfunktion ist, dass sie ein Objekt benötigt, auf das reagiert werden muss - und C weiß nichts über Objekte.

Der einfachste Weg wäre folgendes zu tun:

//In a header file: 
extern "C" int e(int * k, int * e); 

//In your implementation: 
int e(int * k, int * e) { return 0; } 
+0

also meinst du es nicht zu einer Mitgliedsfunktion machen? – Methos

+0

In diesem Fall ja. IMO die größere Einfachheit durch die Verwendung einer Standalone-Funktion überwiegt die fehlende Kapselung beteiligt. – PaulJWilliams

7

Das Problem ist, dass method = Funktion!. Der Compiler wird Ihre Methode, um so etwas verwandeln:

int e(A *this, int *k, int *j); 

Also, es ist sicher, man kann es nicht passieren, weil die Klasseninstanz nicht als Argument übergeben werden kann. Eine Möglichkeit, um zu arbeiten, besteht darin, die Methode als statisch zu gestalten, damit sie den guten Typ hat. Aber es gibt keine Klasseninstanz und keinen Zugriff auf nicht statische Klassenmitglieder.

Die andere Möglichkeit besteht darin, eine Funktion mit einem statischen Zeiger auf ein A zu initialisieren, das beim ersten Mal initialisiert wird. Die Funktion leitet den Anruf nur an die Klasse um:

int callback(int *j, int *k) 
{ 
    static A *obj = new A(); 
    a->(j, k); 
} 

Dann können Sie die Callback-Funktion registrieren.

13

Sie können dies auch tun, wenn die Member-Funktion ist nicht statisch, aber es erfordert eine Arbeit Bit (siehe auch Convert C++ function pointer to c function pointer):

#include <stdio.h> 
#include <functional> 

template <typename T> 
struct Callback; 

template <typename Ret, typename... Params> 
struct Callback<Ret(Params...)> { 
    template <typename... Args> 
    static Ret callback(Args... args) {      
     func(args...); 
    } 
    static std::function<Ret(Params...)> func; 
}; 

template <typename Ret, typename... Params> 
std::function<Ret(Params...)> Callback<Ret(Params...)>::func; 

void register_with_library(int (*func)(int *k, int *e)) { 
    int x = 0, y = 1; 
    int o = func(&x, &y); 
    printf("Value: %i\n", o); 
} 

class A { 
    public: 
     A(); 
     ~A(); 
     int e(int *k, int *j); 
}; 

typedef int (*callback_t)(int*,int*); 

A::A() { 
    Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2); 
    callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);  
    register_with_library(func);  
} 

int A::e(int *k, int *j) { 
    return *k - *j; 
} 

A::~A() { } 

int main() { 
    A a; 
} 

Dieses Beispiel ist vollständig in dem Sinne, dass es kompiliert:

g++ test.cpp -std=c++11 -o test 

Sie benötigen die c++11 Flagge. Im Code sehen Sie, dass register_with_library(func) aufgerufen wird, wobei func eine statische Funktion ist, die dynamisch an die Elementfunktion e gebunden ist.

+0

Cool! Ich wollte schon immer wissen, wie ich das mache. – Jacko

+0

Was passiert, wenn der C-Callback die Form int __stdcall callback (int *, int *) hat? – Jacko

+0

@Jacko. Mmm ... das ist Callee/Anrufer verantwortlich für Stack Cleanup, oder? Ich weiß nicht ... Ich habe alles über Windows vergessen. :-) –

Verwandte Themen