429 votes

Fonctions de rappel en C++

En C++, quand et comment utiliser une fonction de rappel ?

EDIT :
J'aimerais voir un exemple simple pour écrire une fonction de rappel.

3 votes

[Ceci] ( thispointer.com/ ) explique très bien les principes de base des fonctions de rappel et permet de comprendre facilement le concept.

179voto

Ramon Zarazua Points 2144

Il y a aussi la façon C de faire des callbacks : les pointeurs de fonction.

//Define a type for the callback signature,
//it is not necessary, but makes life easier

//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*CallbackType)(float);  

void DoWork(CallbackType callback)
{
  float variable = 0.0f;

  //Do calculations

  //Call the callback with the variable, and retrieve the
  //result
  int result = callback(variable);

  //Do something with the result
}

int SomeCallback(float variable)
{
  int result;

  //Interpret variable

  return result;
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWork(&SomeCallback);
}

Maintenant, si vous voulez passer des méthodes de classe en tant que callbacks, les déclarations de ces pointeurs de fonction sont plus complexes, par exemple :

//Declaration:
typedef int (ClassName::*CallbackType)(float);

//This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
  //Class instance to invoke it through
  ClassName objectInstance;

  //Invocation
  int result = (objectInstance.*callback)(1.0f);
}

//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
  //Class pointer to invoke it through
  ClassName * pointerInstance;

  //Invocation
  int result = (pointerInstance->*callback)(1.0f);
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWorkObject(&ClassName::Method);
  DoWorkPointer(&ClassName::Method);
}

2 votes

Il y a une erreur dans l'exemple de méthode de classe. L'invocation devrait être : (instance.*callback)(1.0f)

0 votes

Merci de l'avoir souligné. Je vais ajouter les deux pour illustrer l'invocation par un objet et par un pointeur d'objet.

3 votes

Cela présente l'inconvénient, par rapport à std::tr1:function, que le rappel est typé par classe ; cela rend peu pratique l'utilisation de rappels de style C lorsque l'objet effectuant l'appel ne connaît pas la classe de l'objet à appeler.

79voto

Karl von Moor Points 4550

Scott Meyers donne un bel exemple :

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
  typedef std::function<int (const GameCharacter&)> HealthCalcFunc;

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
  : healthFunc(hcf)
  { }

  int healthValue() const { return healthFunc(*this); }

private:
  HealthCalcFunc healthFunc;
};

Je pense que l'exemple dit tout.

std::function<> est la façon "moderne" d'écrire des callbacks en C++.

4 votes

Par curiosité, dans quel livre SM donne-t-il cet exemple ? Merci :)

6 votes

Je sais que c'est vieux, mais parce que j'ai presque commencé à faire cela et cela a fini par ne pas fonctionner sur ma configuration (mingw), si vous utilisez une version de GCC < 4.x, cette méthode n'est pas supportée. Certaines des dépendances que j'utilise ne compilent pas sans beaucoup de travail dans la version de GCC >= 4.0.1, donc je suis coincé avec l'utilisation des bons vieux callbacks de style C, qui fonctionnent très bien.

38voto

Reed Copsey Points 315315

A Fonction de rappel est une méthode qui est passée dans une routine, et appelée à un moment donné par la routine à laquelle elle est passée.

C'est très utile pour créer des logiciels réutilisables. Par exemple, de nombreuses API de systèmes d'exploitation (telles que l'API Windows) font un usage intensif des callbacks.

Par exemple, si vous souhaitez travailler avec des fichiers dans un dossier, vous pouvez appeler une fonction de l'API, avec votre propre routine, et votre routine est exécutée une fois par fichier dans le dossier spécifié. Cela permet à l'API d'être très flexible.

72 votes

Cette réponse ne dit pas vraiment au programmeur moyen ce qu'il ne savait pas. J'apprends le C++ tout en étant familier avec de nombreux autres langages. Ce qu'est le callback en général ne me concerne pas.

0 votes

La question porte sur la manière d'utiliser les rappels, et non sur la manière de les définir.

12voto

Darryl Points 2560

Il n'existe pas de concept explicite de fonction de rappel en C++. Les mécanismes de callback sont souvent implémentés via des pointeurs de fonction, des objets functor ou des objets callback. Les programmeurs doivent concevoir et implémenter explicitement la fonctionnalité de callback.

Modifier en fonction des réactions :

Malgré les réactions négatives que cette réponse a reçues, elle n'est pas fausse. Je vais essayer de mieux expliquer mon point de vue.

Le C et le C++ disposent de tout ce dont vous avez besoin pour mettre en œuvre des fonctions de rappel. La façon la plus courante et la plus triviale d'implémenter une fonction de rappel est de passer un pointeur de fonction comme argument de fonction.

Toutefois, les fonctions de rappel et les pointeurs de fonction ne sont pas synonymes. Un pointeur de fonction est un mécanisme du langage, tandis qu'une fonction de rappel est un concept sémantique. Les pointeurs de fonction ne sont pas la seule façon d'implémenter une fonction de rappel - vous pouvez également utiliser des foncteurs et même des fonctions virtuelles. Ce qui fait d'un appel de fonction un callback n'est pas le mécanisme utilisé pour identifier et appeler la fonction, mais le contexte et la sémantique de l'appel. Dire que quelque chose est une fonction de rappel implique une séparation plus grande que la normale entre la fonction appelante et la fonction spécifique appelée, un couplage conceptuel plus lâche entre l'appelant et l'appelé, l'appelant ayant un contrôle explicite sur ce qui est appelé. C'est cette notion floue de couplage conceptuel plus lâche et de sélection de la fonction par l'appelant qui fait de quelque chose une fonction de callback, et non l'utilisation d'un pointeur de fonction.

Par exemple, la documentation .NET pour IFormatProvider dit que "GetFormat est une méthode de rappel" même s'il ne s'agit que d'une méthode d'interface banale. Je ne pense pas que quiconque puisse soutenir que tous les appels de méthodes virtuelles sont des fonctions de rappel. Ce qui fait de GetFormat une méthode de rappel n'est pas la mécanique de la façon dont elle est transmise ou invoquée, mais la sémantique de l'appelant qui choisit la méthode GetFormat de l'objet qui sera appelée.

Certains langages incluent des fonctionnalités avec une sémantique de rappel explicite, généralement liée aux événements et à la gestion des événements. Par exemple, C# dispose de la fonction événement dont la syntaxe et la sémantique sont explicitement conçues autour du concept de rappel. Visual Basic a son Poignées qui déclare explicitement qu'une méthode est une fonction de rappel tout en faisant abstraction du concept de délégués ou de pointeurs de fonction. Dans ces cas, le concept sémantique d'un callback est intégré dans le langage lui-même.

Le C et le C++, en revanche, n'intègrent pas l'élément concept sémantique de fonctions de rappel de manière presque aussi explicite. Les mécanismes sont là, la sémantique intégrée ne l'est pas. Vous pouvez très bien implémenter des fonctions de callback, mais pour obtenir quelque chose de plus sophistiqué qui inclut une sémantique de callback explicite, vous devez le construire au-dessus de ce que C++ fournit, comme ce que Qt a fait avec son programme Signaux et fentes .

En bref, le C++ dispose de tout ce dont vous avez besoin pour implémenter des callbacks, souvent assez facilement et trivialement en utilisant des pointeurs de fonction. Ce qu'il ne possède pas, ce sont des mots-clés et des fonctionnalités dont la sémantique est spécifique aux callbacks, tels que soulever , émettent , Poignées , événement += etc. Si vous venez d'un langage comportant ces types d'éléments, la prise en charge native des rappels en C++ vous semblera réduite à néant.

1 votes

Heureusement que ce n'est pas la première réponse que j'ai lue lorsque j'ai visité cette page, sinon j'aurais immédiatement rebondi !

6voto

AudioDroid Points 1185

Les fonctions de rappel font partie de la norme C, et donc aussi du C++. Mais si vous travaillez avec C++, je vous conseille d'utiliser la fonction Schéma d'observation à la place : http://en.wikipedia.org/wiki/Observer_pattern

1 votes

Les fonctions de rappel ne sont pas nécessairement synonymes d'exécution d'une fonction via un pointeur de fonction passé en argument. Selon certaines définitions, le terme "fonction de rappel" comporte la sémantique supplémentaire de la notification à un autre code de quelque chose qui vient de se produire, ou du moment où quelque chose doit se produire. De ce point de vue, une fonction de rappel ne fait pas partie de la norme C, mais peut être facilement mise en œuvre en utilisant des pointeurs de fonction, qui font partie de la norme.

3 votes

"fait partie de la norme C, et fait donc également partie de C++". C'est un malentendu typique, mais un malentendu quand même :-)

0 votes

Je suis d'accord. Je vais laisser les choses en l'état, car cela ne fera qu'accroître la confusion si je les modifie maintenant. Je voulais dire que les pointeurs de fonction ( !) font partie de la norme. Dire autre chose que cela - je suis d'accord - est trompeur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X