web-dev-qa-db-fra.com

Utilisation d'une fonction membre de classe C ++ comme fonction de rappel C

J'ai une bibliothèque C qui a besoin d'une fonction de rappel pour être enregistrée pour personnaliser certains traitements. Le type de la fonction de rappel est int a(int *, int *).

J'écris du code C++ similaire au suivant et j'essaie d'enregistrer une fonction de classe C++ comme 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 renvoie 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 sur http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html . Mais à mon avis, cela ne résout pas le problème)
  2. Existe-t-il un moyen alternatif/meilleur de résoudre ce problème?
43
Methos

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 class A* qui correspond au pointeur this. C'est pourquoi vous ne pouvez les enregistrer que si la signature du rappel a également le premier paramètre class A* type.

41
sharptooth

Vous pouvez également le faire si la fonction membre n'est pas statique, mais elle nécessite un peu plus de travail (voir aussi Convertir le 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ée, où func est une fonction statique liée dynamiquement à la fonction membre e.

17
Anne van Rossum

Le problème est que la méthode! = Fonction. Le compilateur transformera votre méthode en quelque chose comme ça:

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

Donc, c'est sûr que vous ne pouvez pas le passer, car l'instance de classe ne peut pas être passée en argument. Une façon de contourner le problème est de rendre la méthode statique, de cette façon, elle aurait le bon type. Mais il n'y aura aucune instance de classe et accès aux membres de classe non statiques.

L'autre façon consiste à déclarer une fonction avec un pointeur statique sur un A initialisé la première fois. La fonction redirige uniquement l'appel vers la classe:

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

Ensuite, vous pouvez enregistrer la fonction de rappel.

6
Raoul Supercopter

Eh bien ... si vous êtes sur une plate-forme win32, il y a toujours la mauvaise façon de Thunking ...

Thunking dans Win32: simplification des rappels aux fonctions membres non statiques

C'est une solution mais je ne recommande pas de l'utiliser.
Il a une bonne explication et c'est bien de savoir qu'il existe.

5
TimW

Le problème avec l'utilisation d'une fonction membre est qu'elle a besoin d'un objet sur lequel agir - et C ne connaît pas les 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; }
1
PaulJWilliams

Dans cette solution, nous avons une classe de modèle avec la méthode statique à attribuer à la "fonction c" comme rappel. Cette classe contient un objet "ordinaire" (avec une fonction membre nommée callback () qui sera finalement appelée).

Une fois que votre classe (ici, A) est définie, elle peut être facilement utilisée:

int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

Exemple complet:

#include <iostream>

// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
  static inline HeldObjectType object;

  static void callback( ) {
    object.callback();
  } // ()

  HeldObjectType &  operator() ( ) {
    return object;
  }

  Holder( HeldObjectType && obj )
  {
    object = obj;
  }

  Holder() = delete;

}; // class

// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);

// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
  f();
} // ()

// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
  std::cout << "I'm fun\n";
} // 

// ----------------------------------------------------------
// 
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
// 
// ----------------------------------------------------------
class A {
private:
  int n;

public:

  A(  ) : n( 0 ) { }

  A( int a, int b ) : n( a+b ) { }

  void callback( ) {
    std::cout << "A's callback(): " << n << "\n";
  }

  int getN() {
    return n;
  }

}; // class

// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()
0
cibercitizen1