Cos’è un delegato C ++?

Qual è l’idea generale di un delegato in C ++? Cosa sono, come vengono utilizzati e a cosa servono?

Mi piacerebbe prima conoscerli in un modo ‘scatola nera’, ma anche un po ‘di informazioni sul coraggio di queste cose sarebbe grandioso.

Questo non è C ++ al suo livello più puro o più pulito, ma noto che il codice base in cui lavoro li ha in abbondanza. Spero di capirli abbastanza, quindi posso semplicemente usarli e non dover approfondire l’orribile template annidato.

Questi due articoli del The Code Project spiegano cosa intendo ma non particolarmente succintamente:

  • Membri Funzione Puntatori e il più veloce ansible delegati C ++

  • I delegati del C ++ impossibilmente veloci

Hai un incredibile numero di scelte per raggiungere i delegati in C ++. Ecco quelli che mi sono venuti in mente.


Opzione 1: funtori:

Un object funzione può essere creato implementando l’ 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); 

Opzione 2: espressioni lambda (solo C ++ 11 )

 // 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); 

Opzione 3: puntatori di funzione

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

Opzione 4: puntatore alle funzioni membro (soluzione più veloce)

Vedi Delegato Fast C ++ (in The Code Project ).

 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); 

Opzione 5: std :: function

(o boost::function se la tua libreria standard non lo supporta). È più lento, ma è il più flessibile.

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

Opzione 6: associazione (utilizzando std :: bind )

Consente di impostare alcuni parametri in anticipo, comodo per chiamare una funzione membro, ad esempio.

 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 

Opzione 7: modelli

Accetta qualsiasi cosa purché corrisponda alla lista degli argomenti.

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

Un delegato è una class che include un puntatore o un riferimento a un’istanza dell’object, un metodo membro della class di quell’object da chiamare su quell’istanza dell’object e fornisce un metodo per triggersre tale chiamata.

Ecco un esempio:

 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 ); } 

La necessità di implementazioni di delegati C ++ è un imbarazzo duraturo per la comunità C ++. Ad ogni programmatore C ++ piacerebbe averli, quindi alla fine li usano nonostante i fatti che:

  1. std::function() usa operazioni di heap (ed è fuori portata per la programmazione embedded seria).

  2. Tutte le altre implementazioni fanno concessioni verso la portabilità o la conformità standard a gradi più o meno grandi (si prega di verificare ispezionando le varie implementazioni delegate qui e su codeproject). Devo ancora vedere un’implementazione che non usa reinterpret_casts selvaggi, “prototipi” di classi annidate che sperano di produrre puntatori di funzione della stessa dimensione di quello passato dall’utente, trucchi del compilatore come prima dichiarazione in avanti, poi typedef e poi di nuovo dichiarazione, questa volta eredita da un’altra class o simili tecniche ombrose. Mentre è un grande risultato per gli implementatori che lo hanno costruito, è ancora una triste testimonianza di come C ++ si evolve.

  3. Solo raramente è stato sottolineato che, ora oltre 3 revisioni standard C ++, i delegati non sono stati adeguatamente indirizzati. (O la mancanza di funzionalità linguistiche che consentono implementazioni semplici dei delegati).

  4. Con il modo in cui le funzioni lambda del C ++ 11 sono definite dallo standard (ogni lambda ha un tipo diverso anonimo), la situazione è migliorata solo in alcuni casi d’uso. Ma per il caso d’uso dell’utilizzo dei delegati nelle API della libreria (DLL), i soli lambda non sono ancora utilizzabili. La tecnica comune qui è quella di impacchettare prima il lambda in una funzione std :: e poi passarlo attraverso l’API.

Molto semplicemente, un delegato fornisce funzionalità su come un puntatore a funzione DOVREBBE funzionare. Ci sono molte limitazioni dei puntatori di funzione in C ++. Un delegato usa un po ‘di malvagità nei modelli dietro le quinte per creare una funzione tipo puntatore-tipo di template che funzioni nel modo in cui si vorrebbe.

cioè – puoi impostarli per puntare a una data funzione e puoi passarli in giro e chiamarli quando e dove vuoi.

Ci sono alcuni esempi molto buoni qui:

Un’opzione per i delegati in C ++ che non è altrimenti menzionata qui è di fare lo stile C usando una funzione ptr e un argomento di contesto. Questo è probabilmente lo stesso schema che molti che fanno questa domanda stanno cercando di evitare. Ma il modello è portatile, efficiente ed è utilizzabile nel codice incorporato e nel 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); ... 

Equivalente a Windows Runtime di un object funzione in C ++ standard. Si può usare l’intera funzione come parametro (in realtà si tratta di un puntatore a funzione). Viene utilizzato principalmente in concomitanza con gli eventi. Il delegato rappresenta un contratto che i gestori di eventi soddisfano molto. Semplifica il modo in cui un puntatore di funzione può funzionare.