How to convert a lambda to an std::function using templates

35,400

Solution 1

You can't pass a lambda function object as an argument of type std::function<T> without explicitly specifying the template argument T. Template type deduction tries to match the type of your lambda function to the std::function<T> which it just can't do in this case - these types are not the same. Template type deduction doesn't consider conversions between types.

It is possible if you can give it some other way to deduce the type. You can do this by wrapping the function argument in an identity type so that it doesn't fail on trying to match the lambda to std::function (because dependent types are just ignored by type deduction) and giving some other arguments.

template <typename T>
struct identity
{
  typedef T type;
};

template <typename... T>
void func(typename identity<std::function<void(T...)>>::type f, T... values) {
  f(values...);
}

int main() {
  func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8);
  return 0;
}

This is obviously not useful in your situation though because you don't want to pass the values until later.

Since you don't want to specify the template parameters, nor do you want to pass other arguments from which the template parameters can be deduced, the compiler won't be able to deduce the type of your std::function argument.

Solution 2

You can use a dedicated/retrospective cast. Once you have a tool like this

#include <functional>

using namespace std;

template<typename T>
struct memfun_type
{
    using type = void;
};

template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
    using type = std::function<Ret(Args...)>;
};

template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
    return func;
}

you can say FFL() to all lambda types to have them converted to what would be the correct version of std::function

template <typename... Args> void Callback(std::function<void(Args...)> f){
    // store f and call later
}

int main()
{
    Callback(FFL([](int a, float b){
        // do something
    }));

    return 0;
}

Display

Solution 3

As shown at Inferring the call signature of a lambda or arbitrary callable for "make_function", you can infer the calling signature of a lambda (or any other functor with a single calling signature) from its (single) operator():

template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

template<typename T>
struct get_signature_impl { using type = typename remove_class<
    decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;

This is a rather inflexible approach, though; as R. Martinho Fernandes says, it won't work for functors with multiple operator()s, nor for functors with templated operator() or for (C++14) polymorphic lambdas. This is why bind defers inference of its result type until the eventual call attempt.

Solution 4

It is possible to get the needed std::function type for lambda using derivation, decltype, variadic templates and a few type traits:

namespace ambient {

    template <typename Function>
    struct function_traits : public function_traits<decltype(&Function::operator())> {};

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType(ClassType::*)(Args...) const> {
        typedef ReturnType (*pointer)(Args...);
        typedef const std::function<ReturnType(Args...)> function;
    };

    template <typename Function>
    typename function_traits<Function>::function to_function (Function& lambda) {
        return static_cast<typename function_traits<Function>::function>(lambda);
    }

    template <class L>
    struct overload_lambda : L {
        overload_lambda(L l) : L(l) {}
        template <typename... T>
        void operator()(T&& ... values){
            // here you can access the target std::function with
            to_function(*(L*)this)(std::forward<T>(values)...);
        }
    };

    template <class L>
    overload_lambda<L> lambda(L l){
        return overload_lambda<L>(l);
    }

}

I use it in my code like this:

ambient::lambda([&](const vector<int>& val){ // some code here // })(a);

PS: in my real case I then save this std::function object and its arguments inside a generic kernel objects that I can execute later on demand via virtual functions.

     

Solution 5

Isn't currying already implemented with std::bind?

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );
Share:
35,400
retep998
Author by

retep998

I am a Rust and C++ programmer that uses primarily Windows. I am also a fluffy bunny.

Updated on July 05, 2022

Comments

  • retep998
    retep998 almost 2 years

    Basically, what I want to be able to do is take a lambda with any number of any type of parameters and convert it to an std::function. I've tried the following and neither method works.

    std::function([](){});//Complains that std::function is missing template parameters
    template <typename T> void foo(function<T> f){}
    foo([](){});//Complains that it cannot find a matching candidate
    

    The following code does work however, but it is not what I want because it requires explicitly stating the template parameters which does not work for generic code.

    std::function<void()>([](){});
    

    I've been mucking around with functions and templates all evening and I just can't figure this out, so any help would be much appreciated.

    As mentioned in a comment, the reason I'm trying to do this is because I'm trying to implement currying in C++ using variadic templates. Unfortunately, this fails horribly when using lambdas. For example, I can pass a standard function using a function pointer.

    template <typename R, typename...A>
    void foo(R (*f)(A...)) {}
    void bar() {}
    int main() {
        foo(bar);
    }
    

    However, I can't figure out how to pass a lambda to such a variadic function. Why I'm interested in converting a generic lambda into an std::function is because I can do the following, but it ends up requiring that I explicitly state the template parameters to std::function which is what I am trying to avoid.

    template <typename R, typename...A>
    void foo(std::function<R(A...)>) {}
    int main() {
        foo(std::function<void()>([](){}));
    }
    
  • retep998
    retep998 over 11 years
    My goal was to be able to do something like this cout<<curry([](int x, int y){return x*y;})(5)(7);. I already know how to use std::bind and have used it extensively before. This problem on the other hand is simply a challenge to see what I can accomplish with variadic templates.
  • Steve Jessop
    Steve Jessop over 11 years
    @retep998: in your example code, your lambda captures nothing and therefore is convertible to a function pointer. I assume that you don't want a solution that relies on that?
  • retep998
    retep998 over 11 years
    Ah well. I might as well just constraint myself to simple function pointers, because apparently lambdas just won't work for my purposes. Though I do hope they provide some way to deal with lambdas in variadic templates in a future standard.
  • retep998
    retep998 over 11 years
    Oh jeez... I think I'll just stay away from lambdas for a while. They're becoming more trouble than they're worth.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 11 years
    @retep998: Other than the word curry vs. std::bind and the placeholder, how does the proposed solution differ from your request? cout << std::bind([](int x, int y){ return x*y; },5,_1)(7)
  • Luc Danton
    Luc Danton over 11 years
    Arguably std::bind is partial application (amongst other uses). There is an overlap in what both currying and partial application are useful for though.
  • xtofl
    xtofl over 11 years
  • Bartek Banachewicz
    Bartek Banachewicz about 11 years
    crap. I thought that it will be unambiguous since lambdas have only one op().
  • Manu343726
    Manu343726 almost 10 years
    Note that my traits are very similar to the solutions proposed here. I was posting this only to show you a practical and working example of something similar to what you are trying to do.
  • Avio
    Avio almost 7 years
    This FFL thing is beautiful! Changing the prototype of Callback with: template <typename... Args> auto parameter_grinding_callback(std::function<void(Args...)> f) -> decltype(f) one can then write: auto omg = parameter_grinding_callback(FFL([](int a){std::cout << a << std::endl;})); omg(5);
  • Danny S
    Danny S about 3 years
    This is a brilliant answer. I had been passing lambdas to a variadic std::function arg without issue on Windows (Visual Studio 2015) but hit compile errors with gcc on Linux. By implementing the above solution, I can keep my code changes minimal and the look is fairly clean.