2008-09-30 6 views
13

J'ai un pointeur de fonction définie par:Appel d'un pointeur de fonction C++ sur une instance d'objet spécifique

typedef void (*EventFunction)(int nEvent); 

Y at-il un moyen de gérer cette fonction avec une instance spécifique d'un objet C++?

class A 
{ 
private: 
    EventFunction handler; 

public: 
    void SetEvent(EventFunction func) { handler = func; } 

    void EventOne() { handler(1); } 
}; 

class B 
{ 
private: 
    A a; 
public: 
    B() { a.SetEvent(EventFromA); } // What do I do here? 

    void EventFromA(int nEvent) { // do stuff } 
}; 

Edit: Orion a les options Boost offres telles que:

boost::function<int (int)> f; 
X x; 
f = std::bind1st(
     std::mem_fun(&X::foo), &x); 
f(5); // Call x.foo(5) 

Malheureusement Boost est pas une option pour moi. Y at-il une sorte de "currying" fonction qui peut être écrit en C++ qui fera ce genre de retour d'un pointeur sur une fonction membre dans un pointeur de fonction normale?

+0

Oui, il y a une fonction C++ taitement, appelé TR1 :: bind ou boost :: bind! Et oui il y a un emballage pour tenir le résultat au curry. C'est ce qu'on appelle tr1 :: function ou boost :: function. – Aaron

+0

Quelqu'un peut-il s'il vous plaît fournir une réponse d'extrait de code en utilisant la fonction std :: C++ 11? J'apprends le C++ et c'est confus pour moi, car je m'attends à quelque chose d'aussi basique, mais toutes les réponses ci-dessous qui reviennent à booster ou C++ 11 ne fournissent pas vraiment un extrait de code/howto. – Dima

Répondre

9

Je recommande vivement Don L'excellente bibliothèque FastDelegate de Clugston. Il fournit toutes les choses que vous attendez d'un vrai délégué et compile jusqu'à quelques instructions ASM dans la plupart des cas. L'article d'accompagnement est une bonne lecture sur les pointeurs de fonction de membre aussi bien.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

+0

Beaucoup de bonnes informations là-bas. – fryguybob

+0

Je dois noter qu'avec le support C++ 11, vous devriez utiliser 'std :: function' si possible. FastDelegate n'a pas été mis à jour depuis un certain temps et ne fonctionnera probablement pas pour toujours. –

1

Malheureusement, le type EventFunction ne peut pointer vers une fonction de B, car ce type n'est pas correct. Vous pouvez faire le bon type, mais qui est sans doute pas vraiment la solution que vous voulez:

typedef void (*B::EventFunction)(int nEvent);

... puis tout fonctionne une fois que vous appelez la fonction de rappel avec un obhect de B. Mais vous avez probablement vouloir être capable d'appeler des fonctions en dehors de B, dans d'autres classes qui font d'autres choses. C'est en quelque sorte le point d'un rappel. Mais maintenant ce type pointe vers quelque chose de définitivement dans B. Les solutions les plus attrayantes sont:

  • Faites de B une classe de base, puis écrasez une fonction virtuelle pour chaque autre classe qui pourrait être appelée. A stocke alors un pointeur sur B au lieu d'un pointeur de fonction. Beaucoup plus propre.
  • Si vous ne voulez pas lier la fonction à un type de classe spécifique, même une classe de base (et je ne vous blâmerais pas), alors je vous suggère de faire de la fonction appelée une fonction statique: "static void EventFrom A(int nEvent);" . Alors vous pouvez l'appeler directement, sans un objet de B. Mais vous voulez probablement qu'il appelle une instance spécifique de B (à moins que B ne soit un singleton). Par conséquent, si vous voulez pouvoir appeler une instance spécifique de B, mais pouvoir aussi appeler des non-B, vous devez passer quelque chose d'autre à votre fonction de rappel pour que la fonction de rappel puisse appeler le bon objet . Faites de votre fonction un static, comme ci-dessus, et ajoutez un paramètre void * que vous allez faire un pointeur vers B.

En pratique, vous voyez deux solutions à ce problème: les systèmes ad hoc où vous passez un void * et l'événement et les hiérarchies avec des fonctions virtuelles dans une classe de base, comme les systèmes de fenêtrage

2

Lisez à propos de pointers to members. Pour appeler une méthode sur la classe dérivée, la méthode doit être déclarée dans la classe de base comme virtuelle et redéfinie dans la classe de base et votre pointeur doit pointer vers la méthode de la classe de base. En savoir plus sur pointers to virtual members.

0

On ne sait pas très bien ce que vous essayez d'accomplir ici. ce qui est clair, c'est que les pointeurs de fonction ne sont pas le moyen. Peut-être que ce que vous cherchez est un pointeur vers la méthode.

+0

Deuxième point. Si vous fournissez plus d'informations, nous pourrions vous fournir plus d'aide. –

11

Exécuter à partir des pointeurs de fonction C++ brutes et utiliser std::function à la place.

Vous pouvez utiliser boost::function si vous utilisez un ancien compilateur tel que Visual Studio 2008 qui ne prend pas en charge C++ 11. Sont la même chose - ils ont tiré un peu de choses boost dans la bibliothèque std pour C++ 11.

Note: vous pouvez lire la documentation de fonction boost au lieu de celui de Microsoft car il est plus facile de comprendre

+0

Ou tr1 :: function et tr1 :: bind. – luke

+0

... qui sont la même chose :) – jkp

11

Vous pouvez utiliser des pointeurs de fonction à l'index dans le vtable d'une instance d'un objet. C'est ce qu'on appelle un member function pointer. Votre syntaxe aurait besoin de changer d'utiliser les et les « & :: » opérateurs « *. »:

class A; 
class B; 
typedef void (B::*EventFunction)(int nEvent) 

puis:

class A 
{ 
private: 
    EventFunction handler; 

public: 
    void SetEvent(EventFunction func) { handler = func; } 

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*" 
}; 

class B 
{ 
private: 
    A a; 
public: 
    B() { a.SetEvent(&B::EventFromA); } // note: "&::" 

    void EventFromA(int nEvent) { /* do stuff */ } 
}; 
0

J'ai un ensemble de classes pour cette chose exacte que J'utilise dans mon framework C++.

http://code.google.com/p/kgui/source/browse/trunk/kgui.h

Comment je gère est chaque fonction de classe qui peut être utilisé comme un rappel a besoin d'une fonction statique qui lie le type d'objet à elle. J'ai un ensemble de macros qui le font automatiquement. Il fait une fonction statique avec le même nom, sauf avec un préfixe "CB_" et un premier paramètre supplémentaire qui est le pointeur d'objet de classe.

Contrôlez les types de classe kGUICallBack et diverses versions de modèles pour gérer différentes combinaisons de paramètres.

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();} 
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);} 
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);} 
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);} 
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);} 
2

Si vous interfacer avec une bibliothèque C, vous ne pouvez pas utiliser une fonction de membre de la classe sans utiliser quelque chose comme boost::bind. La plupart des bibliothèques C qui prennent une fonction de rappel habituellement vous permettent également de passer un argument supplémentaire de votre choix (généralement de type void*), que vous pouvez utiliser pour amorcer votre classe, comme si:


class C 
{ 
public: 
    int Method1(void) { return 3; } 
    int Method2(void) { return x; } 

    int x; 
}; 

// This structure will hold a thunk to 
struct CCallback 
{ 
    C *obj; // Instance to callback on 
    int (C::*callback)(void); // Class callback method, taking no arguments and returning int 
}; 

int CBootstrapper(CCallback *pThunk) 
{ 
    // Call the thunk 
    return ((pThunk->obj) ->* (pThunk->callback))(/* args go here */); 
} 

void DoIt(C *obj, int (C::*callback)(void)) 
{ 
    // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it 
    struct CCallback thunk = {obj, callback}; 
    foobar(&CBootstrapper, &thunk); 
} 

int main(void) 
{ 
    C c; 
    DoIt(&c, &C::Method1); // Essentially calls foobar() with a callback of C::Method1 on c 
    DoIt(&c, &C::Method2); // Ditto for C::Method2 
} 
1

Vous mentionnez que boost est pas une option pour vous, mais avez-vous TR1 à votre disposition? TR1 propose des objets function, bind et mem_fn basés sur la bibliothèque boost, et il est possible qu'il soit déjà fourni avec votre compilateur. Ce n'est pas encore standard, mais au moins deux compilateurs que j'ai utilisés récemment l'ont eu.

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx

+0

Bonnes recommandations, mais mon projet spécifique est le code VC6 hérité. C'est aussi pourquoi je ne peux pas vraiment résoudre le problème avec de meilleurs choix de conception C++ * sigh *. – fryguybob

Questions connexes