Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create functor that wraps lambda with captured variable?

How has the functor below to be altered to work as a lambda wrapper?

template<typename T>
class F {
  T f;
public:
  F(T t){
      f = t;
  }
  T& operator()(){
    return f;
  }
};

int main()
{
    int x = 5;
    F<int (*)(int, int)> f( [x](int a, int b){return a+b;} );
    return 0;
}

The compiler says

error: no matching function for call to 'F<int (*)(int, int)>::F(main()::<lambda(int, int)>)'
     F<int (*)(int, int)> f( [x](int a, int b){return a+b;} );
like image 801
mskr Avatar asked Apr 11 '16 00:04

mskr


People also ask

Can lambdas be templated?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.

Is lambda function a functor?

We have seen that lambda is just a convenient way to write a functor, therefore we should always think about it as a functor when coding in C++. We should use lambdas where we can improve the readability of and simplify our code such as when writing callback functions.

What is the difference between functor and lambda?

Functors allow to write functional programs in C++. Lambdas are syntactic sugar to simplify this.

How do you pass a lambda function as an argument?

Passing Lambda Expressions as Arguments You can pass lambda expressions as arguments to a function. If you have to pass a lambda expression as a parameter, the parameter type should be able to hold it. If you pass an integer as an argument to a function, you must have an int or Integer parameter.


1 Answers

It's more complicated... Internally lambda functions that capture variables are not functions as such, are data structures. I not found any solution developed and many requests and questions unresolved, then I developed this minimal code to wrap lambda pointer not using std::function or any other standard function or dependence. Pure c++11.

Accepts all kinds of lambda captures, arguments by reference, return void, and supports top level functions and member methods.

// Type checkers
template<typename _Type>
struct IsVoid
{
    static const bool value = false;
};

template<>
struct IsVoid<void>
{
    static const bool value = true;
};

// Callable signature interfce
template<typename _ReturnType, typename..._ArgTypes>
struct Callable
{
    typedef _ReturnType ReturnType;
    typedef _ReturnType (*SignatureType)(_ArgTypes...);

    virtual _ReturnType operator()(_ArgTypes...args) = 0;
};

// Function and lambda closure wrapper
template<typename _ClosureType, typename _ReturnType, typename..._ArgTypes>
struct Closure: public Callable<_ReturnType, _ArgTypes...>
{
    typedef _ClosureType ClosureType;

    const _ClosureType closureHandler;

    Closure(const _ClosureType& handler)
        : closureHandler(handler)
    {
    }

    _ReturnType operator()(_ArgTypes...args) override
    {
        if(IsVoid<_ReturnType>::value)
            closureHandler(args...);
        else
            return closureHandler(args...);
    }
};

// Fuction template selector
template <typename _FunctionType>
class Function
    : public Function<decltype(&_FunctionType::operator())>
{
};

// Function, lambda, functor...
template <typename _ReturnType, typename... _ArgTypes>
class Function<_ReturnType(*)(_ArgTypes...)>
{
public:
    typedef Function<_ReturnType(*)(_ArgTypes...)> SelfType;
    typedef _ReturnType(*SignatureType)(_ArgTypes...); 
    Callable<_ReturnType, _ArgTypes...>* callableClosure;

    Function(_ReturnType(*function)(_ArgTypes...))
        : callableClosure(new Closure<SignatureType, _ReturnType, _ArgTypes...>(function))
    {
    }

    // Captured lambda specialization
    template<typename _ClosureType>
    Function(const _ClosureType& function)
        : callableClosure(new Closure<decltype(function), _ReturnType, _ArgTypes...>(function))
    {
    }

    _ReturnType operator()(_ArgTypes... args)
    {
        if(IsVoid<_ReturnType>::value)
            (*callableClosure)(args...);
        else
            return (*callableClosure)(args...);
    }
};

// Member method
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes>
class Function<_ReturnType(_ClassType::*)(_ArgTypes...)>
{
public:
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...)> SelfType;
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...);

    SignatureType methodSignature;

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...))
        : methodSignature(method)
    {
    }

    _ReturnType operator()(_ClassType* object, _ArgTypes... args)
    {
        if(IsVoid<_ReturnType>::value)
            (object->*methodSignature)(args...);
        else
            return (object->*methodSignature)(args...);
    }
};

// Const member method
template <typename _ClassType, typename _ReturnType, typename... _ArgTypes>
class Function<_ReturnType(_ClassType::*)(_ArgTypes...) const>
{
public:
    typedef Function<_ReturnType(_ClassType::*)(_ArgTypes...) const> SelfType;
    typedef _ReturnType(_ClassType::*SignatureType)(_ArgTypes...) const;

    SignatureType methodSignature;

    Function(_ReturnType(_ClassType::*method)(_ArgTypes...) const)
        : methodSignature(method)
    {
    }

    _ReturnType operator()(_ClassType* object, _ArgTypes... args)
    {
        if(IsVoid<_ReturnType>::value)
            (object->*methodSignature)(args...);
        else
            return (object->*methodSignature)(args...);
    }
};

Tests:

#include <iostream>

class Foo
{
public:
    int bar(int a, int b)
    {
        return a + b;
    }
};

int someFunction(int a, int b)
{
    return a + b;
}

int main(int argc, char** argv) 
{
    int a = 10;
    int b = 1;

    // Lambda without capturing
    Function<int(*)(int)> fn1([] (int b) -> int {
        return b;
    });

    std::cout << fn1(2) << std::endl; // 2

    // Lambda capturing variable
    Function<int(*)(int)> fn2([a] (int c) -> int {
        return a + c;
    });

    std::cout << fn2(-7) << std::endl; // 3

    // Lambda capturing scope
    Function<int(*)(int)> fn3([&] (int c) -> int {
        return a + c;
    });

    std::cout << fn3(-5) << std::endl; // 5

    // Arguments by reference 
    Function<void(*)(int&, int)> fn4([] (int& d, int f) {
        d = d + f;
    });

    fn4(a, -3); // Void call

    std::cout << a << std::endl; // 7

    // Top level function reference
    Function<int(*)(int, int)> fn6(someFunction);

    std::cout << fn6(a, 4) << std::endl; // 11

    // Member method
    Foo* foo = new Foo();
    Function<int(Foo::*)(int,int)> fn7(foo->bar);
    std::cout << fn7(foo, a, 8) << std::endl; // 15
}

Works correctly wih gcc 4.9.

Thanks for your question.

like image 174
joas Avatar answered Sep 23 '22 17:09

joas