Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pass lambda to function that accepts function with templated types

I'm attempting to make a template function that takes a function as a parameter and the parameter function has arguments for the template to deduce.

Example time:

Here is a function that accepts a fixed function type and works

void func_a(void(*func)(int)) {
    func(1);
}
int main() {
    auto f = [](int x) -> void { printf("%i\n", x); };
    func_a(f);
    return 0;
}

Here is what I want to do, expanding on the first example (this won't compile)

template <typename... T>
void func_b(void(*func)(T...)) {
    func(1);
}
int main() {
    auto f = [](int x) -> void { printf("%i\n", x); };
    func_b(f);       // neither of
    func_b<int>(f); // these work
    return 0;
}

Ideally I'd like func_b to accept both a regular function and a lambda function like func_a does, but with template magics.

like image 461
MichaelMitchell Avatar asked Sep 14 '16 09:09

MichaelMitchell


2 Answers

Unfortunately, template deduction doesn't work well with implicit conversions. However, you can convert the lambda explicitly into a function pointer type. The shortest, but somewhat confusing way to do that is to apply unary + operator:

func_b(+f);

Or you could use the more intuitive, but also verbose and DRY-violating cast operation:

func_b(static_cast<void(*)(int)>(f));

But perhaps, you'll want to simply accept any callable type, instead of only function pointers:

template <class Fun>
void func_c(Fun&& func) {
    func(1);
}

This works fine with lambdas. No conversions involved.

func_c(f);

And it also works for capturing lambdas (that cannot be converted to function pointers), std::function and function objects such as those defined in <functional>.

like image 69
eerorika Avatar answered Nov 13 '22 00:11

eerorika


here you have some options that work:

#include <functional>
template <typename T>
void func_b(T&& func) {
    func(1);
}

template <typename... T>
void func_c(std::function<void(T...)> func) {
    func(1);
}    

template <typename... T>
void func_d1(std::function<void(T...)> func) {
    func(1);
}   

template<typename... Params>
using fun = std::function<void(Params...)>;

template <typename T, typename... P>
void func_d(T& func) {
    func_d1(fun<P...>(func));
}

int main() {
    auto f = [](int x) { printf("%i\n", x); };
    func_b(f);
    func_b(std::function<void(int)>(f));

    func_c(std::function<void(int)>(f));

    func_d<decltype(f),int>(f);
    return 0;
}

The issue is: A lambda is not a function pointer or std::function-object.

func_b uses perfect forwarding so T will be the type of the lambda not a std::function object.

For func_c. You should not convert a lambda to a c-style function pointer. A std::function object is able to do this conversion but only explicitly (by design) so you need to explicitly convert them.

func_d (and func_d1) combine the other aspects. It forwards the lambda and makes a std::function-object explicitly out of it though it needs an additional template parameter.

like image 34
Hayt Avatar answered Nov 13 '22 01:11

Hayt