Is it possible to use the new lambda expressions in Visual C++ 2010 as CLR event handlers? I\'ve tried the following code:
SomeEvent += gcnew EventHandler(
The following is my solution that allows one to wrap lambdas (as well as any function objects - i.e. anything on which operator() can be called) into delegates. It has some limits - specifically, it doesn't support delegates with tracking reference parameters (% in C++/CLI, ref/out in C#); and it has an upper limit on the number of parameters the delegate can take (because VC++2010 doesn't suppport vararg templates) - though the code can be trivially adjusted to support up to as many as you want.
#pragma once
#include
#include
namespace detail
{
struct return_type_helper
{
private:
template
struct dependent_false { enum { value = false }; };
template
struct illegal_delegate_type
{
static_assert(dependent_false::value, "Delegates with more than 2 parameters, or with parameters of tracking reference types (T%), are not supported.");
};
struct anything
{
template
operator T() const;
};
public:
template
static decltype(static_cast(nullptr)()) dummy(int(*)[1]);
template
static decltype(static_cast(nullptr)(anything())) dummy(int(*)[2]);
template
static decltype(static_cast(nullptr)(anything(), anything())) dummy(int(*)[3]);
template
static illegal_delegate_type dummy(...);
};
template::value == std::tr1::alignment_of::value)>
struct aligner
{
static_assert(Match, "Function object has unsupported alignment");
};
template
struct aligner
{
typedef Aligner type;
};
template
struct aligner : aligner
{
};
template
struct aligner : aligner
{
};
template
struct aligner : aligner
{
};
template
struct aligner : aligner
{
};
template
struct aligner : aligner
{
};
template
struct aligner : aligner
{
};
template
ref class lambda_wrapper
{
public:
lambda_wrapper(const F& f)
{
pin_ptr pf = (interior_ptr)&f_storage;
new(pf) F(f);
}
~lambda_wrapper()
{
pin_ptr pf = (interior_ptr)&f_storage;
pf->~F();
}
template
operator D^ ()
{
D^ d = nullptr;
return gcnew D(this, &lambda_wrapper::invoke(0))>);
}
private:
template
[System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Sequential, Size = sizeof(T))]
value struct embedded_storage
{
private:
typename aligner::type dummy;
};
embedded_storage f_storage;
template
R invoke()
{
pin_ptr pf = (interior_ptr)&f_storage;
return (*pf)();
}
template
R invoke(A1 a1)
{
pin_ptr pf = (interior_ptr)&f_storage;
return (*pf)(a1);
}
template
R invoke(A1 a1, A2 a2)
{
pin_ptr pf = (interior_ptr)&f_storage;
return (*pf)(a1, a2);
}
};
}
template
detail::lambda_wrapper^ make_delegate(F f)
{
return gcnew detail::lambda_wrapper(f);
}
Sample usage:
Func^ f2 = make_delegate([&](int x, String^ y) -> int {
Console::WriteLine("Func {0} {1}", x, y);
return 2;
});
While this technically does what you want, the practical applications are somewhat limited due to the fact that C++0x lambdas are expanded into plain classes, not ref or value ones. Since plain classes cannot contain managed types in C++/CLI (i.e. no members of object handle type, no members of tracking reference type, and no members of value class type), this means that lambdas cannot capture any variables of those types, either. There is no workaround I'm aware of for tracking references. For value class, you can take an unmanaged pointer to it (pin_ptr if needed), and capture that.
For object handles, you can store them in gcroot, and capture that - but there are severe performance implications - in my tests, accessing a member via gcroot is about 40x times slower than doing it using a plain object handle. It's actually not much in absolute measure for a single call, but for something that is called repeatedly in a loop - say, most LINQ algorithms - it would be a killer. But note that this only applies when you need to capture a handle in the lambda! If you just use it to write a predicate inline, or to update a counter, it'll work just fine.