2009-06-16 22 views
34

Tengo una biblioteca C que necesita una función de devolución de llamada para registrarse para personalizar algunos procesos. El tipo de la función de devolución de llamada es int a(int *, int *).Utilizando una función de miembro de clase C++ como función de devolución de llamada C

Estoy escribiendo código C++ similar al siguiente y tratar de registrar una función de C++ Class como la función de devolución de llamada:

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

} 

El compilador lanza siguiente error:

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

Mis preguntas:

  1. Antes que nada es posible registrar una función de memeber de clase C++ como lo estoy intentando hacer y si es así h ¿Ay? (Leí 32.8 en http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Pero, en mi opinión, no resuelve el problema)
  2. ¿Existe alguna forma alternativa/mejor de abordar esto?

Respuesta

37

Puede hacerlo si la función de miembro es estática.

funciones miembro no estáticos de la clase A tienen un primer parámetro implícito de tipo class A* que corresponde a este puntero. Es por eso que solo puede registrarlos si la firma de la devolución de llamada también tiene el primer parámetro del tipo class A*.

+0

sí. esa solución funcionó. Lo que me confunde es que el compilador no mostró el error int (A ::) (A *, int *, int *) 'no concuerda' int() (int, int *) ' – Methos

+0

Lo hizo, pero al ponerlo (A: :) lo que significa que la función es parte de la clase A, que a partir de ahí implica el puntero 'this'. – GManNickG

+0

Solo tengo curiosidad ... ¿está esto especificado en el estándar? Solo eché un vistazo a la sección de clases y no encontré esto. Sin embargo, muy interesante. Simplemente no creo que cada compilador necesariamente tenga que manejar funciones de miembros no estáticos de esta manera. – Tom

1

El problema con el uso de una función miembro es que necesita un objeto sobre el que actuar, y C no conoce los objetos.

La forma más sencilla sería la de hacer lo siguiente:

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

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

, ¿quiere decir que no se convierta en una función miembro? – Methos

+0

En este caso, sí. IMO la mayor simplicidad que ofrece el uso de una función independiente supera la falta de encapsulación involucrada. – PaulJWilliams

7

El problema es que el método de la función =!. El compilador va a transformar su método para algo así:

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

lo tanto, es seguro que no se puede pasar, porque la instancia de clase no se puede pasar como argumento. Una forma de evitar el problema es hacer que el método sea estático, de esta forma tendría el tipo correcto. Pero no habrá ninguna instancia de clase ni acceso a miembros de clase no estáticos.

La otra forma es declarar una función con un puntero estático a una A inicializada la primera vez. La función solo redirige la llamada a la clase:

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

Luego puede registrar la función de devolución de llamada.

13

También puede hacer esto si la función miembro no es estática, sino que requiere un poco más de trabajo (véase también 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; 
} 

Este ejemplo es completa en el sentido de que compila:

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

Necesitará la bandera c++11. En el código, verá que se llama al register_with_library(func), donde func es una función estática vinculada dinámicamente a la función miembro e.

+0

¡Genial! Siempre quise saber cómo hacer esto. – Jacko

+0

¿Qué ocurre si la devolución de llamada C tiene la forma int __stdcall callback (int *, int *)? – Jacko

+0

@Jacko. Mmm ... se trata de callee/invocador responsable de la limpieza de la pila, ¿no? No sé ... Olvidé todo sobre Windows. :-) –

Cuestiones relacionadas