Reputation: 69
I am learning parallel programming and meta-programming at the moment and I have struggle making compile time check for lambda that captures variable.
Let us say that we have code like this one:
#include <type_traits>
#include <functional>
#include <iostream>
int main(){
auto non_capture = [](){
std::cout<<"this lambda is not capturing!"<<std::endl;
};
auto capture = [&](){
std::cout<<"this lambda is capturing"<<std::endl;
};
//value is true for this one
static_assert(std::is_convertible<decltype(non_capture), std::function<void()>>::value, "message1");
//i need help for this
static_assert(is_lambda_that_captures<decltype(capture)>::value, "this is some error message");
}
I need help to define is_lambda_that_captures type trait so I can pass the second static assert. How can I do it?
Having a solution for the latest standard is more than welcome(current latest is c++17) :)
Upvotes: 0
Views: 642
Reputation: 10315
For C++17 You can leverage the latest trait: std::is_invocable
. It should work fine for both capturing and non-capturing lambda:
static_assert(std::is_invocable_v<decltype(noncapture)>,"Error?!");
static_assert(std::is_invocable_v<decltype(capture)>,"Error?!");
Of course it will work also for function pointers, std::function
and all objects with operator()
accessible. If You want to detect only lambdas (for which I don't see much use), You will need more trait magic.
Upvotes: 2
Reputation: 29017
Firstly, note that
static_assert(
std::is_convertible<decltype(capture), std::function<void()>>::value,
"msg");
would also pass. Any lambda can be converted into a std::function
of the appropriate type. The difference between a capturing lambda and a non-capturing lambda is that a non-capturing lambda can be converted into a pointer to function. So:
static_assert(
std::is_convertible<decltype(non_capture), void (*)()>>::value,
"msg");
will pass, but
static_assert(
std::is_convertible<decltype(capture), void (*)()>>::value,
"msg");
will fail. You can distinguish between a non-capturing lambda and an actual function pointer with std::is_class
(but I can't think why you would want to).
Upvotes: 1
Reputation: 69902
Here's my solution. It relies on two principles:
non-capuring lambdas are convertible to function pointers.
All lambdas are convertible to std::function.
#include <type_traits>
#include <functional>
#include <iostream>
template<class T, class Ret, class...Args>
struct is_non_capture_lambda : std::is_convertible<T, Ret (*)(Args...)>
{
};
template<class T, class Ret, class...Args>
struct is_capture_lambda
{
static constexpr auto convertible = std::is_convertible<T, std::function<Ret(Args...)>>::value;
using type = bool;
static constexpr auto value = convertible && !is_non_capture_lambda<T, Ret, Args...>::value;
};
int main(){
auto non_capture = [](){
std::cout<<"this lambda is not capturing!"<<std::endl;
};
auto capture = [&](){
std::cout<<"this lambda is capturing"<<std::endl;
};
//value is true for this one
static_assert(is_non_capture_lambda<decltype(non_capture), void>::value, "message1");
//i need help for this
static_assert(is_capture_lambda<decltype(capture), void>::value, "this is some error message");
}
Upvotes: 6