Qu’est-ce qu’un délégué C ++?

Quelle est l’idée générale d’un délégué en C ++? Qu’est-ce qu’ils sont, comment sont-ils utilisés et à quoi servent-ils?

J’aimerais d’abord en savoir plus à leur sujet dans une «boîte noire», mais un peu d’information sur les entrailles de ces choses serait également très bien.

Ce n’est pas le C ++ à l’état le plus pur ou le plus propre, mais je remarque que le code où je travaille les a en abondance. J’espère les comprendre suffisamment pour que je puisse les utiliser et ne pas avoir à plonger dans la terrible horreur du modèle nested.

Ces deux articles The Code Project expliquent ce que je veux dire mais pas de manière particulièrement succincte:

  • Pointeurs de fonctions membres et delegates les plus rapides possibles en C ++

  • Les delegates C ++ incroyablement rapides

Vous avez un nombre incroyable de choix pour atteindre les delegates en C ++. Voici celles qui me sont venues à l’esprit.


Option 1: foncteurs:

Un object de fonction peut être créé par l’ operator() implémentation operator()

 struct Functor { // Normal class/struct members int operator()(double d) // Arbitrary return types and parameter list { return (int) d + 1; } }; // Use: Functor f; int i = f(3.14); 

Option 2: expressions lambda ( C ++ 11 uniquement)

 // Syntax is roughly: [capture](parameter list) -> return type {block} // Some shortcuts exist auto func = [](int i) -> double { return 2*i/1.15; }; double d = func(1); 

Option 3: pointeurs de fonction

 int f(double d) { ... } typedef int (*MyFuncT) (double d); MyFuncT fp = &f; int a = fp(3.14); 

Option 4: pointeur vers les fonctions membres (solution la plus rapide)

Voir Délégué Fast C ++ (sur le projet de code ).

 struct DelegateList { int f1(double d) { } int f2(double d) { } }; typedef int (DelegateList::* DelegateType)(double d); DelegateType d = &DelegateList::f1; DelegateList list; int a = (list.*d)(3.14); 

Option 5: std :: function

(ou boost::function si votre bibliothèque standard ne le supporte pas). C’est plus lent, mais c’est le plus flexible.

 #include  std::function f = [can be set to about anything in this answer] // Usually more useful as a parameter to another functions 

Option 6: liaison (en utilisant std :: bind )

Permet de définir certains parameters à l’avance, ce qui est pratique pour appeler une fonction membre par exemple.

 struct MyClass { int DoStuff(double d); // actually a DoStuff(MyClass* this, double d) }; std::function f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1); // auto f = std::bind(...); in C++11 

Option 7: modèles

Acceptez n’importe quoi tant qu’il correspond à la liste d’arguments.

 template  int DoSomething(FunctionT func) { return func(3.14); } 

Un délégué est une classe qui encapsule un pointeur ou une référence à une instance d’object, une méthode membre de la classe de cet object à appeler sur cette instance d’object et fournit une méthode pour déclencher cet appel.

Voici un exemple:

 template  class CCallback { public: typedef void (T::*fn)( int anArg ); CCallback(T& trg, fn op) : m_rTarget(trg) , m_Operation(op) { } void Execute( int in ) { (m_rTarget.*m_Operation)( in ); } private: CCallback(); CCallback( const CCallback& ); T& m_rTarget; fn m_Operation; }; class A { public: virtual void Fn( int i ) { } }; int main( int /*argc*/, char * /*argv*/ ) { A a; CCallback cbk( a, &A::Fn ); cbk.Execute( 3 ); } 

Le besoin d’implémentations de delegates C ++ est une gêne de longue durée pour la communauté C ++. Tous les programmeurs C ++ adoreraient les avoir, alors ils les utiliseraient malgré les faits:

  1. std::function() utilise des opérations de tas (et est hors de scope pour une programmation embarquée sérieuse).

  2. Toutes les autres implémentations font des concessions à la portabilité ou à la conformité standard à des degrés plus ou moins élevés (veuillez vérifier en inspectant les différentes implémentations des delegates ici et sur codeproject). Je n’ai pas encore vu d’implémentation qui n’utilise pas les reinterpret_casts sauvages, les “prototypes” de classe Nested qui, espérons-le, produiront des pointeurs de la même taille que ceux transmis par l’utilisateur. héritant cette fois d’une autre classe ou de techniques similaires. Bien que ce soit un grand accomplissement pour les développeurs qui l’ont construit, c’est un sortingste témoignage sur l’évolution du C ++.

  3. Il est rare que l’on remarque que plus de 3 révisions standard C ++ ont été apscopes aux delegates. (Ou le manque de fonctionnalités de langage qui permettent des implémentations simples des delegates.)

  4. Avec la manière dont les fonctions lambda C ++ 11 sont définies par le standard (chaque lambda est de type anonyme, différent), la situation ne s’est améliorée que dans certains cas d’utilisation. Mais pour le cas d’utilisation des delegates dans les API de bibliothèque (DLL), les lambdas seuls ne sont toujours pas utilisables. La technique courante consiste à placer d’abord le lambda dans une fonction std :: et à le transmettre à travers l’API.

Très simplement, un délégué fournit des fonctionnalités sur la façon dont un pointeur de fonction DEVRAIT fonctionner. Il existe de nombreuses limitations des pointeurs de fonctions en C ++. Un délégué utilise des modèles en arrière-plan nastyness pour créer un object de type pointeur-fonction de type template qui fonctionne comme vous le souhaitez.

c.-à-d. – vous pouvez les définir pour pointer une fonction donnée et vous pouvez les transmettre et les appeler quand et où vous voulez.

Il y a de très bons exemples ici:

Une option pour les delegates en C ++ qui n’est pas mentionnée ici est de le faire en utilisant un ptr de fonction et un argument de contexte. C’est probablement le même schéma que beaucoup de personnes qui posent cette question essaient d’éviter. Mais, le modèle est portable, efficace et est utilisable dans le code embarqué et le kernel.

 class SomeClass { in someMember; int SomeFunc( int); static void EventFunc( void* this__, int a, int b, int c) { SomeClass* this_ = static_cast< SomeClass*>( this__); this_->SomeFunc( a ); this_->someMember = b + c; } }; void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext); ... SomeClass* someObject = new SomeObject(); ... ScheduleEvent( SomeClass::EventFunc, someObject); ... 

Windows Runtime équivalent d’un object fonction en C ++ standard. On peut utiliser la fonction entière en tant que paramètre (en fait, c’est un pointeur de fonction). Il est principalement utilisé en conjonction avec des événements. Le délégué représente un contrat que les gestionnaires d’événements remplissent beaucoup. Cela facilite la façon dont un pointeur de fonction peut fonctionner.