I have some code that generates assembly for a JIT idea I'm working on. I use meta-programming to generate calls by analyzing the function type and then generating the correct assembly to call it. I recently wanted to add lambda support, and lambdas have two versions, non-capturing (normal __cdecl function call) and capturing (__thiscall, member-function call with the lambda object as context).
__thiscall is slightly more expensive so I'd like to avoid it whenever possible, and I'd also like to avoid having to use different call generation functions depending on the lambda type.
I tried many ways to detect the lambda type via templates and SFINAE and all failed.
Non-capturing lambdas have an ::operator function_type*
which one can use to convert them to function pointers, while capturing lambdas don't.
Relevant C++ spec: http://en.cppreference.com/w/cpp/language/lambda
Any ideas?
edit I'd like to have a solution that works for vs 2013/2015, gcc and clang
Test code follows
#include <utility>
//this doesn't work
template < class C, class T >
struct HasConversion {
static int test(decltype(std::declval<C>().operator T*, bool()) bar) {
return 1;
}
static int test(...) {
return 0;
}
};
template <class C>
void lambda_pointer(C lambda) {
int(*function)() = lambda;
printf("Lambda function: %p without context\n", function);
}
template <class C>
void lambda_pointer_ctx(C lambda) {
int(C::*function)() const = &C::operator();
void* context = λ
printf("Lambda function: %p with context: %p\n", function, context);
}
int main() {
int a;
auto l1 = [] {
return 5;
};
auto l2 = [a] {
return a;
};
//non capturing case
//works as expected
lambda_pointer(l1);
//works as expected (ctx is meaningless and not used)
lambda_pointer_ctx(l1);
//lambda with capture (needs context)
//fails as expected
lambda_pointer(l1);
//works as expected (ctx is a pointer to class containing the captures)
lambda_pointer_ctx(l1);
/*
//this doesn't work :<
typedef int afunct() const;
HasConversion<decltype(l1), afunct>::test(0);
HasConversion<decltype(l2), afunct>::test(0);
*/
return 0;
}
std::is_assignable<void(*&)(int), decltype(lambda)>{}
, or, in your casestd::is_assignable<afunct*&, decltype(lambda)>{}
withtypedef void afunct(int);
– Tolerable