Usando una función miembro de la clase C ++ como una función de devolución de llamada C

StackOverflow https://stackoverflow.com/questions/1000663

  •  05-07-2019
  •  | 
  •  

Pregunta

Tengo una biblioteca de C que necesita que se registre una función de devolución de llamada para personalizar algunos procesos. El tipo de la función de devolución de llamada es int a (int *, int *) .

Estoy escribiendo un código C ++ similar al siguiente e intento registrar una función de clase C ++ 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 arroja el siguiente error:

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

Mis preguntas:

  1. En primer lugar, ¿es posible registrar una función de miembro de la clase C ++ como la que estoy tratando de hacer y, de ser así, cómo? (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 una forma alternativa / mejor de abordar esto?
¿Fue útil?

Solución

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

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

Otros consejos

También puede hacer esto si la función miembro no es estática, pero requiere un poco más de trabajo (consulte también Convertir el puntero de la función C ++ al puntero de la función c ):

#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 completo en el sentido de que se compila:

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

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

El problema es que el método! = función. El compilador transformará tu método en algo así:

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

Por lo tanto, es seguro que no puedes pasarlo, porque la instancia de la clase no se puede pasar como argumento. Una forma de evitar esto es hacer que el método sea estático, de esta manera tendría el tipo correcto. Pero no lo hará ninguna instancia de clase, y el acceso a los miembros de la clase no estática.

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 puedes registrar la función de devolución de llamada.

Bueno ... si estás en una plataforma win32, siempre existe la forma desagradable de Thunking ...

Thunking en Win32: Simplificación de devoluciones de llamada a funciones miembro no estáticas

Es una solución, pero no recomiendo su uso.
Tiene una buena explicación y es bueno saber que existe.

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 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; }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top