Utilisation d'une fonction membre de la classe C ++ en tant que fonction de rappel C
Question
Je dispose d’une bibliothèque C qui nécessite l’enregistrement d’une fonction de rappel pour personnaliser certains traitements. Le type de la fonction de rappel est int a (int *, int *)
.
J'écris un code C ++ similaire au suivant et essaie d'enregistrer une fonction de classe C ++ en tant que fonction de rappel:
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()
{
}
Le compilateur génère l'erreur suivante:
In constructor 'A::A()',
error:
argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.
Mes questions:
- Tout d’abord, est-il possible d’enregistrer une fonction memeber de classe C ++ comme j’essaie de le faire et si oui comment? (J'ai lu 32.8 à l'adresse http: //www.parashift. com / c ++ - faq-lite / mix-c-and-cpp.html . Mais, à mon avis, cela ne résout pas le problème)
- Existe-t-il un moyen alternatif / meilleur de s’y attaquer?
La solution
Vous pouvez le faire si la fonction membre est statique.
Les fonctions membres non statiques de la classe A ont un premier paramètre implicite de type classe A *
qui correspond à ce pointeur . C'est pourquoi vous ne pouvez les enregistrer que si la signature du rappel comporte également le premier paramètre de type classe A *
.
Autres conseils
Vous pouvez également le faire si la fonction membre n'est pas statique, mais qu'elle nécessite un peu plus de travail (voir aussi Convertir un pointeur de fonction C ++ en pointeur de fonction 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;
}
Cet exemple est complet dans le sens où il compile:
g++ test.cpp -std=c++11 -o test
Vous aurez besoin du drapeau c ++ 11
. Dans le code, vous voyez que register_with_library (func)
est appelé, où func
est une fonction statique liée dynamiquement à la fonction membre e
.
Le problème est que cette méthode! = fonction. Le compilateur transformera votre méthode en quelque chose comme ça:
int e( A *this, int *k, int *j );
Donc, il est certain que vous ne pouvez pas le transmettre car l'instance de la classe ne peut pas être passée en argument. Une façon de contourner le problème consiste à rendre la méthode statique, ce qui donnerait le bon type. Mais cela n’aura aucune instance de classe, ni accès à des membres de classe non statiques.
L’autre méthode consiste à déclarer une fonction avec un pointeur statique sur un A initialisé la première fois. La fonction ne redirige l'appel vers la classe que:
int callback( int *j, int *k )
{
static A *obj = new A();
a->(j, k);
}
Ensuite, vous pouvez enregistrer la fonction de rappel.
Eh bien ... si vous êtes sur une plate-forme win32, il y a toujours la mauvaise manière de Thunking ...
Apprendre à utiliser Win32: simplification des rappels vers des fonctions membres non statiques
C’est une solution mais je ne recommande pas de l’utiliser.
Il a une bonne explication et il est bon de savoir qu’il existe.
Le problème avec l’utilisation d’une fonction membre est qu’elle a besoin d’un objet sur lequel agir - et que C n’a pas connaissance des objets.
Le moyen le plus simple serait de procéder comme suit:
//In a header file:
extern "C" int e(int * k, int * e);
//In your implementation:
int e(int * k, int * e) { return 0; }