2009-06-16 20 views
34

Mam bibliotekę C, która potrzebuje funkcji wywołania zwrotnego do zarejestrowania, aby dostosować niektóre przetwarzanie. Typ funkcji zwrotnej to int a(int *, int *).Używanie funkcji członka klasy C++ jako funkcji wywołania zwrotnego C

piszę C++ kod podobny do poniższego i próbować zarejestrować C++ funkcją klasy jako funkcji callback:

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() 
{ 

} 

Kompilator generuje następujący błąd:

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

moje pytania:

  1. Przede wszystkim można zarejestrować funkcję pamięci klas C++, tak jak ja próbuję zrobić, a jeśli tak, to ow? (Czytałem 32,8 przy http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Ale moim zdaniem to nie rozwiązuje problemu)
  2. Czy istnieje alternatywny/lepszy sposób rozwiązania tego problemu?

Odpowiedz

37

Możesz to zrobić, jeśli funkcja elementu jest statyczna.

Niestatyczne funkcje składowe klasy A mają domyślny pierwszy parametr typu class A*, który odpowiada temu wskaźnikowi. Dlatego można je zarejestrować tylko wtedy, gdy podpis wywołania zwrotnego miał również pierwszy parametr typu class A*.

+0

tak. to rozwiązanie zadziałało. Co mnie myli, to kompilator nie pokazuje błędu int (A ::) (A *, int *, int *) "nie pasuje" int() (int, int *) ' – Methos

+0

Zrobił to, ale przez umieszczenie (Odp .: :) co oznacza, że ​​funkcja jest częścią klasy A, która z tego miejsca implikuje "ten" wskaźnik. – GManNickG

+0

Jestem po prostu ciekawy ... czy jest to określone w standardzie? Po prostu rzuciłem okiem na sekcję o klasach i nie znalazłem tego. Niemniej jednak bardzo interesujące. Po prostu nie sądzę, że każdy kompilator musi w ten sposób obsługiwać niestatyczne funkcje członkowskie. – Tom

1

Problem z używaniem funkcji składowej polega na tym, że potrzebuje obiektu, na którym działa - a C nie wie o obiektach.

Najprostszym sposobem byłoby wykonać następujące czynności:

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

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

, więc masz na myśli, nie sprawiają, że funkcja członka? – Methos

+0

W tym przypadku tak. IMO większa prostota zapewniana przez użycie funkcji samodzielnej przeważa nad brakiem enkapsulacji. – PaulJWilliams

7

Problemem jest to, że metoda = function!. Kompilator będzie przekształcić swoje metody, aby coś takiego:

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

Tak, to na pewno nie można przejść, bo instancja klasy nie może być przekazany jako argument. Jednym ze sposobów obejścia tego problemu jest uczynienie tej metody statyczną, tak by miała dobry typ. Ale nie będzie to żadna instancja klasy i dostęp do niestatycznych członków klasy.

Innym sposobem jest zadeklarowanie funkcji ze statycznym wskaźnikiem do A zainicjowanego za pierwszym razem. Funkcja przekierowuje tylko połączenie do klasy:

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

Następnie można zarejestrować funkcję zwrotną.

13

Można też to zrobić, jeśli funkcja członek nie jest statyczna, ale to wymaga trochę więcej pracy (patrz również 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; 
} 

Ten przykład jest kompletne w tym sensie, że kompiluje:

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

Będziesz potrzebować flagi c++11. W kodzie widzisz, że register_with_library(func) jest wywoływana, gdzie func jest funkcją statyczną dynamicznie związaną z funkcją składową e.

+0

Fajnie! Zawsze chciałem wiedzieć, jak to zrobić. – Jacko

+0

Co jeśli callback C ma postać int callback__stdCall (int *, int *)? – Jacko

+0

@Jacko. Mmm ... to jest o kalku/dzwoniącym odpowiedzialnym za porządkowanie stosów, prawda? Nie wiem ... Zapomniałem wszystkiego o systemie Windows. :-) –

Powiązane problemy