5

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;} );
mskr
  • 375
  • 5
  • 14

3 Answers3

4

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.

joas
  • 170
  • 1
  • 5
  • Hum... It is because of code optimization. Without the `g++ -Ox` [it works correctly](http://coliru.stacked-crooked.com/a/ae63c6895124e850). I wll review soon – joas Apr 14 '16 at 00:00
1

A lambda can't directly be converted to a free function pointer if it captures something just because they are two different things.

A lambda with capturing values must save its state somewhere, but a function pointer is just a memory address thus it doesn't provide that functionality. So you would be allowed to do something

static_cast<int(*)(int,int)>([](int a, int b) { return a+b; })

but that's not your case.

Some solutions could be:

  • don't use a function pointer but use a std::function<int(int,int>) instead
  • provide a free function which invokes the lambda (not a good solution in your case, mostly meant to be used to inerface with legacy code I'd say
  • use a template function which provides the wrapping from lambda to function pointer by itself. Similar to the solution proposed here
Community
  • 1
  • 1
Jack
  • 131,802
  • 30
  • 241
  • 343
0

Use simple workaround with decltype.

auto lambda = [x](int a, int b){return a+b;};
F<decltype(lambda)> f(lambda);  // OK

To make it look concise, we can use macro:

#define DECLARE_F(OBJECT, LAMBDA) \
  auto lambda = LAMBDA; \
  F<decltype(lambda)> OBJECT(lambda)

Usage:

DECLARE_F(f, [x](int a, int b){return a+b;}); // 1 per line if used ## __LINE__
iammilind
  • 68,093
  • 33
  • 169
  • 336