다음은 람다 (함수 객체 - 즉 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 알고리즘)은 킬러가됩니다. 그러나 이것은 람다에서 핸들을 캡처해야 할 때만 적용됩니다! 술어를 인라인으로 작성하거나 카운터를 업데이트하는 데 그냥 사용하면 잘 작동합니다.
내 솔루션 "Lambda2Delegate"를 확인하십시오. http://stackoverflow.com/a/26552573/2604941 –