Utilisation d'une fonction membre de la classe C ++ en tant que fonction de rappel C

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

  •  05-07-2019
  •  | 
  •  

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:

  1. 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)
  2. Existe-t-il un moyen alternatif / meilleur de s’y attaquer?
Était-ce utile?

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; }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top