Question

J'ai un pointeur de fonction défini par:

typedef void (*EventFunction)(int nEvent);

Existe-t-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 }
};

Modifier: Orion a souligné les options offertes par Boost, 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 n'est pas une option pour moi. Existe-t-il une sorte de "currying"? fonction qui peut être écrite en C ++ et qui fait ce genre d’habillage d’un pointeur vers une fonction membre vers un pointeur de fonction normal?

Était-ce utile?

La solution

Je recommande vivement l'excellente bibliothèque FastDelegate de Don Clugston. Il fournit tout ce que vous attendez d'un vrai délégué et compile quelques instructions ASM dans la plupart des cas. L'article qui l'accompagne est également une bonne lecture pour les pointeurs de fonctions membres.

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

Autres conseils

Vous pouvez utiliser les pointeurs de fonction pour indexer dans la table virtuelle d'une instance d'objet donnée. Il s’agit d’un pointeur de fonction membre . Votre syntaxe devra être modifiée pour utiliser le ". * & Quot; et le " & amp; :: " opérateurs:

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

et ensuite:

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 */ }
};

Éloignez-vous des pointeurs de fonction C ++ bruts et utilisez 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.
boost: function et std :: function sont la même chose - ils ont inséré beaucoup de choses de boost dans la bibliothèque std pour C ++ 11.

Remarque: vous voudrez peut-être lire la documentation de la fonction boost au lieu de celle de Microsoft car il est plus facile à comprendre

Vous pouvez trouver la FAQ C ++ de Marshall Cline utile. à ce que vous essayez d'accomplir.

En savoir plus sur les pointeurs vers les membres . Pour appeler une méthode sur la classe dérivée, celle-ci doit être déclarée dans la classe de base comme virtuelle et remplacée dans la classe de base et votre pointeur doit pointer vers la méthode de la classe de base. En savoir plus sur les pointeurs vers les membres virtuels .

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


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
}

Malheureusement, le type EventFunction ne peut pas pointer vers une fonction de B, car ce n'est pas le type correct. Vous pouvez en faire le bon type, mais ce n'est probablement pas la solution que vous souhaitez:

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

... et ensuite tout fonctionne une fois que vous appelez le rappel avec un obhect de B. Mais vous voulez probablement pouvoir appeler des fonctions en dehors de B, dans d'autres classes qui font d'autres choses. C'est en quelque sorte le but d'un rappel. Mais maintenant, ce type indique clairement quelque chose en B. Des solutions plus attrayantes sont:

  • Faites de B une classe de base, puis substituez une fonction virtuelle à chaque autre classe pouvant être appelée. A stocke ensuite un pointeur sur B au lieu d'un pointeur de fonction. Beaucoup plus propre.
  • Si vous ne souhaitez pas lier la fonction à un type de classe spécifique, même à une classe de base (et je ne vous blâme pas), je vous suggère de créer la fonction qui s'appelle une fonction statique: " < code> static void EventFrom A (int nEvent); ". Ensuite, vous pouvez l’appeler directement, sans objet de B. Mais vous voudrez probablement qu’elle appelle une instance spécifique de B (sauf si B est un singleton).
  • Donc, 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 afin que la fonction de rappel puisse appeler le bon objet. . Créez une fonction statique, comme ci-dessus, et ajoutez un paramètre void * sur lequel vous allez placer un pointeur sur B.

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

.

Vous dites que le boost n'est pas une option pour vous, mais avez-vous le TR1 à votre disposition?

TR1 propose des objets fonction, bind et mem_fn basés sur la bibliothèque boost, et vous pouvez déjà l'avoir 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

Ce que vous essayez d'accomplir ici n'est pas très clair. ce qui est clair, c’est que les indicateurs de fonction ne sont pas la solution.

Peut-être que ce que vous cherchez, c'est un pointeur sur la méthode.

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 le gère, chaque fonction de classe pouvant être utilisée comme rappel nécessite une fonction statique qui lie le type d'objet à celle-ci. J'ai un ensemble de macros qui le font automatiquement. Il crée une fonction statique portant le même nom, sauf avec un "CB_". préfixe et un premier paramètre supplémentaire qui est le pointeur d'objet de classe.

Consultez les types de classe kGUICallBack et leurs différentes 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);}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top