Nikola Lozanovski
Nikola Lozanovski

Reputation: 69

static asserting lambda that captures

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

Answers (3)

bartop
bartop

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

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

Richard Hodges
Richard Hodges

Reputation: 69902

Here's my solution. It relies on two principles:

  1. non-capuring lambdas are convertible to function pointers.

  2. 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

Related Questions