2010-05-05 5 views
11

Visual C++ 2010에서 CLR 이벤트 처리기로 새 람다 식을 사용할 수 있습니까? 나는 다음과 같은 코드를 시도했다 :Visual C++ 2010의 CLR (.NET) 대리자/이벤트 처리기로 람다 식

그것은 다음과 같은 오류 메시지가 발생
SomeEvent += gcnew EventHandler(
    [] (Object^ sender, EventArgs^ e) { 
     // code here 
    } 
); 

:

오류 C3364 : '시스템 :: 이벤트 핸들러'위임 생성자에 대한 잘못된 인수; 대리자 대상은 멤버 함수에 대한 포인터 여야합니다.

나는 시도가 불가능합니까, 아니면 단순히 구문이 잘못 되었습니까?

+0

내 솔루션 "Lambda2Delegate"를 확인하십시오. http://stackoverflow.com/a/26552573/2604941 –

답변

7

아니요, C++/CLI 컴파일러가 람다 구문을 허용하도록 업데이트되지 않았습니다. 관리 코드가 갖고있는 시작점을 감안할 때 상당히 아이러니 한 btw입니다.

+0

사실입니다. CLR에 존재하지 않는 캡처 절 의미론과 관련이 있는지 궁금합니다. 내가 이해할 수있는 한 C# lambda는'[&, & this]'와 유사하다 - lambda가 부모의 범위에있는 것을 수정할 수 있기 때문에 C++ lambdas에 그러한 캡처 절이 있어야한다는 명령을 내릴 수있다. –

+0

너무 나쁘다. 단지 멤버 함수를 포함하기 위해 핸들러를 생성하는 함수 안에 던져진 클래스를 조금 더러워 보인다. :/ – absence

+0

CLR은 관리되지 않는 포인터를 처리 할 수 ​​있으므로 참조 할 수 있습니다. 나는 그들이 단지 이행하는데 시간이 걸리고 싶어하지 않는다고 생각한다. 연결 기능 요청이이 기록 된 있습니다 : https://connect.microsoft.com/VisualStudio/feedback/details/524356/add-managed-lambdas-to-c-cli - upvote 원할 경우. –

-1

이 문서는 C++에 대한 람다의 몇 가지 예를 가지고 : 실제로 C++0x lambda spec을 구현 같은

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

마이크로 소프트 VS2010 C++ 개선

모습. 따라서 순수하게 관리되지 않으며 유형이 lambda입니다.

CLR lambdas로 C++ lambdas를 사용할 가능성에 대한 힌트는 Microsoft 설명서에는 없습니다. 이 단계에서는 관리되는 대리자의 처리기로 C++ 람다를 사용할 수 없다고 말해야합니다.

9

다음은 람다 (함수 객체 - 즉 operator()을 호출 할 수있는 객체)를 델리게이트로 래핑 할 수있게 해주는 해결책입니다. 여기에는 몇 가지 제한이 있습니다. 즉, 참조 매개 변수를 추적하는 대리자 (C++/CLI의 경우 %, C#의 경우 ref/out)는 지원하지 않습니다. 델리게이트가 취할 수있는 매개 변수의 수에는 상한이 있습니다 (VC++ 2010은 가변 템플릿을 지원하지 않기 때문에). 코드는 원하는만큼 지원할 수 있지만 쉽게 조정할 수 있습니다.

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

샘플 사용 :이 기술적으로 당신이 원하는 것을하는 동안

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

는 실제 응용 프로그램은 다소 C++ 0X 람다가 아닌 일반 클래스로 ref를 확장하거나 있다는 사실로 인해 제한된다 value 개입니다. 일반 클래스는 C++/CLI (관리 객체 유형의 멤버가없고 추적 참조 유형의 멤버가없고 value class 유형의 멤버가 없음)의 관리되는 유형을 포함 할 수 없기 때문에 람다는 이러한 유형의 변수도 캡처 할 수 없습니다. 추적 참조에 대해 알고있는 해결 방법은 없습니다. value class의 경우에는 관리되지 않는 포인터 (필요한 경우 pin_ptr)를 가져 와서 캡처 할 수 있습니다.

개체 핸들의 경우 gcroot<T>에 저장할 수 있으며 성능에 심각한 영향이 있습니다. 내 테스트에서 gcroot<T>을 통해 멤버에 액세스하는 것은 일반 개체 핸들을 사용하는 것보다 약 40 배 느립니다. 실제로는 단일 호출에 대한 절대적인 측정치가 많지 않지만 루프에서 반복적으로 호출되는 것 (예 : 대부분의 LINQ 알고리즘)은 킬러가됩니다. 그러나 이것은 람다에서 핸들을 캡처해야 할 때만 적용됩니다! 술어를 인라인으로 작성하거나 카운터를 업데이트하는 데 그냥 사용하면 잘 작동합니다.

+0

감사! 성능에 미치는 영향을 염두에 두겠습니다. – absence

+0

같은 유형의 lambdas를 호출 할 때 LNK2022와 함께 실패하지만 다른 것들을 캡처하는 것으로 보입니다. 유형이 동일 할 때 생성 된 유형의 크기가 다르다고 가정합니다. 이 문제에 대한 해결책이 있습니까 아니면 어쨌든 내 이론에 근거를두고있는 것입니까? – Sarien

+0

사리 엔, "같은 종류의 람다"가 다른 것을 포착한다는 것은 무엇을 의미합니까? 모든 C++ 람다는 서명이 일치하더라도 캡처 목록이 일치하더라도 고유 한 형식을가집니다. –