2010-05-05 5 views
11

È possibile utilizzare le nuove espressioni lambda in Visual C++ 2010 come gestori di eventi CLR? Ho provato il seguente codice:Espressioni lambda come delegati CLR (.NET)/gestori di eventi in Visual C++ 2010

SomeEvent += gcnew EventHandler(
    [] (Object^ sender, EventArgs^ e) { 
     // code here 
    } 
); 

Essa si traduce nel seguente messaggio di errore:

errore C3364: 'System :: EventHandler': argomento non valido per il costruttore di delegato; bersaglio delegato deve essere un puntatore a una funzione di membro

Am I tentare l'impossibile, o è semplicemente la mia sintassi sbagliata?

+0

Controlla la mia soluzione "Lambda2Delegate" pubblicato qui http://stackoverflow.com/a/26552573/2604941 –

risposta

7

No, il compilatore C++/CLI non è stato aggiornato per accettare la sintassi lambda. Abbastanza ironico visto il vantaggio che il codice gestito aveva.

+0

Lo è davvero. Mi chiedo se questo qualcosa abbia a che fare con la semantica della clausola di cattura, che non esiste in CLR. Per quanto ho capito, C# lambda è qualcosa come '[&, & this]' - come in lambda puoi modificare qualunque cosa si trovi nella portata del suo genitore, in modo che potessero avere semplicemente mandato che i C++ lambda dovessero avere tale clausola di cattura. –

+0

Peccato, sembra un po 'complicato con una classe throw-away all'interno della funzione che crea il gestore, solo per contenere una funzione membro. :/ – absence

+0

CLR può gestire i puntatori non gestiti (che è ciò che viene tradotto e -referenze), quindi potrebbe essere fatto - semplicemente non sarebbe verificabile. Credo che non sia qualcosa che volevano prendersi del tempo per implementare. Ho una richiesta di funzionalità di connessione registrata per questo: https://connect.microsoft.com/VisualStudio/feedback/details/524356/add-managed-lambdas-to-c-cli - upvote se vuoi questo. –

-1

Questa pagina ha alcuni esempi di lambda per C++:

http://msdn.microsoft.com/en-us/library/dd293608%28v=VS.100%29.aspx

miglioramenti Microsoft VS2010 C++ sembrano in realtà attuano C++0x lambda spec. Come tali sono puramente non gestiti e sono di tipo lambda.

Non c'è nulla nella documentazione Microsoft che accenni alla possibilità di usare lambda C++ come lambdas CLR. In questa fase devo dire che non è possibile utilizzare i lambda C++ come gestori per i delegati gestiti.

9

Quanto segue è la mia soluzione che consente di avvolgere lambda (così come qualsiasi oggetto funzione - cioè qualsiasi cosa su cui è possibile chiamare operator()) in delegati. Ha alcuni limiti - in particolare, non supporta i delegati con i parametri di riferimento di tracciamento (% in C++/CLI, ref/out in C#); e ha un limite superiore al numero di parametri che il delegato può prendere (perché VC++ 2010 non supporta i modelli vararg) - sebbene il codice possa essere banalmente aggiustato per supportare fino a quanti ne vuoi.

#pragma once 

#include <new> 
#include <type_traits> 

namespace detail 
{ 
    struct return_type_helper 
    { 
    private: 

     template<class D> 
     struct dependent_false { enum { value = false }; }; 

     template <class D> 
     struct illegal_delegate_type 
     { 
      static_assert(dependent_false<D>::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported."); 
     }; 

     struct anything 
     { 
      template<class T> 
      operator T() const; 
     }; 

    public: 

     template<class D> 
     static decltype(static_cast<D^>(nullptr)()) dummy(int(*)[1]); 

     template<class D> 
     static decltype(static_cast<D^>(nullptr)(anything())) dummy(int(*)[2]); 

     template<class D> 
     static decltype(static_cast<D^>(nullptr)(anything(), anything())) dummy(int(*)[3]); 

     template <class D> 
     static illegal_delegate_type<D> dummy(...); 
    }; 


    template<class Func, class Aligner = char, bool Match = (std::tr1::alignment_of<Func>::value == std::tr1::alignment_of<Aligner>::value)> 
    struct aligner 
    { 
     static_assert(Match, "Function object has unsupported alignment"); 
    }; 

    template<class Func, class Aligner> 
    struct aligner<Func, Aligner, true> 
    { 
     typedef Aligner type; 
    }; 

    template<class Func> 
    struct aligner<Func, char, false> : aligner<Func, short> 
    { 
    }; 

    template<class Func> 
    struct aligner<Func, short, false> : aligner<Func, int> 
    { 
    }; 

    template<class Func> 
    struct aligner<Func, int, false> : aligner<Func, long> 
    { 
    }; 

    template<class Func> 
    struct aligner<Func, long, false> : aligner<Func, long long> 
    { 
    }; 

    template<class Func> 
    struct aligner<Func, long long, false> : aligner<Func, double> 
    { 
    }; 

    template<class Func> 
    struct aligner<Func, double, false> : aligner<Func, void*> 
    { 
    }; 


    template<class F> 
    ref class lambda_wrapper 
    { 
    public: 

     lambda_wrapper(const F& f) 
     { 
      pin_ptr<F> pf = (interior_ptr<F>)&f_storage; 
      new(pf) F(f); 
     } 

     ~lambda_wrapper() 
     { 
      pin_ptr<F> pf = (interior_ptr<F>)&f_storage; 
      pf->~F(); 
     } 

     template <class D> 
     operator D^() 
     { 
      D^ d = nullptr; 
      return gcnew D(this, &lambda_wrapper<F>::invoke<decltype(return_type_helper::dummy<D>(0))>); 
     } 

    private: 

     template<class T> 
     [System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))] 
     value struct embedded_storage 
     { 
     private: 
      typename aligner<T>::type dummy; 
     }; 


     embedded_storage<F> f_storage; 

     template<class R> 
     R invoke() 
     { 
      pin_ptr<F> pf = (interior_ptr<F>)&f_storage; 
      return (*pf)(); 
     } 

     template<class R, class A1> 
     R invoke(A1 a1) 
     { 
      pin_ptr<F> pf = (interior_ptr<F>)&f_storage; 
      return (*pf)(a1); 
     } 

     template<class R, class A1, class A2> 
     R invoke(A1 a1, A2 a2) 
     { 
      pin_ptr<F> pf = (interior_ptr<F>)&f_storage; 
      return (*pf)(a1, a2); 
     } 
    }; 
} 

template<class F> 
detail::lambda_wrapper<F>^ make_delegate(F f) 
{ 
    return gcnew detail::lambda_wrapper<F>(f); 
} 

utilizzo Esempio:

Func<int, String^, int>^ f2 = make_delegate([&](int x, String^ y) -> int { 
    Console::WriteLine("Func {0} {1}", x, y); 
    return 2; 
}); 

Mentre questo tecnicamente ciò che si desidera, le applicazioni pratiche sono piuttosto limitate a causa del fatto che il C++ 0x lambda sono espanse in classi normali, non ref o value quelli. Poiché le classi normali non possono contenere tipi gestiti in C++/CLI (cioè nessun membro del tipo di handle dell'oggetto, nessun membro del tipo di riferimento di tracciamento e nessun membro del tipo value class), ciò significa che lambda non può acquisire alcuna variabile di questi tipi. Non sono disponibili soluzioni per i riferimenti di tracciamento. Per value class, è possibile utilizzare un puntatore non gestito (pin_ptr se necessario) e acquisirlo.

Per le maniglie degli oggetti, è possibile memorizzarli in gcroot<T> e acquisirli, ma ci sono gravi implicazioni sulle prestazioni: nei miei test, l'accesso a un membro tramite gcroot<T> è circa 40 volte più lento rispetto all'utilizzo di un handle di oggetto normale. In realtà non è molto in assoluto per una singola chiamata, ma per qualcosa che viene chiamato ripetutamente in un ciclo - per esempio, la maggior parte degli algoritmi LINQ - sarebbe un killer. Nota che questo si applica solo quando devi catturare una maniglia nella lambda! Se lo usi solo per scrivere un predicato in linea o per aggiornare un contatore, funzionerà perfettamente.

+0

Grazie! Terrò a mente le implicazioni sulle prestazioni. – absence

+0

Questo sembra fallire con LNK2022 se lo chiamo per lambda con lo stesso tipo ma che cattura cose diverse. Suppongo che la dimensione del tipo generato sia diversa mentre il tipo è lo stesso. C'è qualche soluzione per questo o sto solo basandomi sulla mia teoria comunque? – Sarien

+0

Sarien, cosa intendi per "lambda dello stesso tipo che cattura cose diverse"? Ogni lambda C++ ha il suo tipo unico, anche se le firme corrispondono, e anche se le liste di cattura corrispondono. –