5

I'm trying to make a template function to which is possible to pass some other function with any type and number of parameters and bind it to a std::function. I managed to do this:

#include <iostream>
#include <functional>

int foo(int bar)
{
  std::cout << bar << std::endl;
  return bar;
}

template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
{
  return f;
}

int main()
{
  //auto barp = func(foo); // compilation error
  auto bar  = func(std::function<void (int)>(foo));

  bar (0); // prints 0
}

I would like to just call auto barp = func(foo); and have the types deduced, but this line gives the following compilation errors:

error: no matching function for call to ‘func(void (&)(int))’
    auto barp = func(foo);
                        ^
note: candidate is:
note: template<class Ret, class ... Args> std::function<_Res(_ArgTypes ...)> func(std::function<_Res(_ArgTypes ...)>)
 std::function<Ret (Args...)> func(std::function<Ret (Args...)> f)
                              ^
note:   template argument deduction/substitution failed:
note:   mismatched types ‘std::function<_Res(_ArgTypes ...)>’ and ‘int (*)(int)’
   auto barp = func(foo);
                       ^

Why is it trying to match std::function<_Res(_ArgTypes ...)> with int (*)(int)? I feel I should get the compiler somehow to expand _Res(_ArgTypes ...) to int(int), but how?

Tarc
  • 3,214
  • 3
  • 29
  • 41
  • 4
    Why are you converting to a `std::function` at all? `std::function` is a type erasure class: it takes the type information of what it is constructed from, and erases most of it. Type deduction takes its argument, and deduces its type, and generates code. You are asking to deduce what type to erase something down to. That is like making a suit of armor that creates guns to shoot at the wearer; type erasure is the *opposite* of type deduction in most senses. It is very rarely a good idea. It is possible, but C++ has little reason to make it *easy* Do you have a practical use case? – Yakk - Adam Nevraumont Sep 14 '15 at 00:10
  • What about overloads, or variable argument lists? Only in a few circumstances is this possible. You can use things like boost function_traits though. – tahsmith Sep 14 '15 at 00:13
  • @Yakk, I was trying to code a "memoization" function, in the sense that it would return a callable with the same parameters and return type of its argument which would call the original function except that it would look for previous computed values (to be stored in a suitable map). I would use `std::function` to store the original function. Not sure if this is possible though. – Tarc Sep 14 '15 at 00:29
  • 1
    @Tarc Why not just store the original callable object? Note that a function name can have overloads, and a callable object more than one set of legal parameters. See [here](http://stackoverflow.com/a/24641745/1774667) for a one-argument memoizer that only works on function pointers. [here](http://stackoverflow.com/a/25085574/1774667) is a more generic recursive-handling memoizer. – Yakk - Adam Nevraumont Sep 14 '15 at 03:31

3 Answers3

2

A function is not an std::function, it is convertible to one. You can deduce the arguments of a function, however, barring ambiguity about overloads.

#include <iostream>
#include <functional>

int foo(int bar)
{
  std::cout << bar << std::endl;
  return 0;
}

// Will cause error.
//int foo(double); 

template <typename Ret, typename... Args>
std::function<Ret (Args...)> func(Ret f(Args...))
{
  return f;
}

int main()
{
  auto bar = func(foo);
  bar (0); // prints 0
}

What you want to do with the original std::function is similar to this, which more obviously does not work:

template<typename T>
struct A
{
  A(T);  
};

template<typename T>
void func(A<T> a);

int main()
{
    func(42);
}

42 is not a A, it can be converted to one, though. However, converting it to one would require T to already be known.

tahsmith
  • 1,643
  • 1
  • 17
  • 23
  • `&foo` is a little extraneous. A functions name outside of a calling expression is implicitly converted to a function pointer in C/C++. – RamblingMad Sep 14 '15 at 02:50
  • @CoffeeandCode, yeah changed that. I prefer to have it in general, since it is needed for member functions. But here it is just an extra complexity. – tahsmith Sep 14 '15 at 03:24
1

Your code is semantically equivalent to (if it compiled) this:

int foo(int x){
    std::cout << x << std::endl;
    return x;
}

int main(){
    auto bar = [](int x){ return foo(x); };
    bar(0);
}

Apart from the return x part, but that's just me correcting your undefined behaviour.

Your function for returning an std::function is extremely unnecessary, except maybe for the sake of less typing.

You could just as easily use std::functions constructor without the wrapper function.

But, still.

To do what you want to do, you should pass the function pointer itself; without impossible conversion.

try this:

int foo(int x){
    return x + 1;
}

template<typename Ret, typename ... Args>
auto func(Ret(*fp)(Args...)) -> std::function<Ret(Args...)>{
    return {fp};
}

int main(){
    auto bar = func(foo);
    std::cout << bar(0) << std::endl; // outputs '1'
}

The reason your code doesn't work is because of the implicit conversion trying to take place when you pass an argument to func.

As I said, your code currently is semantically equivalent to the example I showed above using lambda expressions. I would strongly recommend just using lambda expressions wherever function wrapping is needed! They're much more flexible and are a core part of the language rather than a library feature.

Remember, non-capturing lambdas are convertible to function pointers; so the following is conforming:

int main(){
    int(*bar)(int) = [](int x){ return x + 1; };
    std::cout << bar(0) << std::endl;
}

and to have similar functionality as you want in your post, we could write something like this:

int main(){
    auto func = +[](int x){ return x + 1; };

    std::cout << "foo(5) = " << func(5) << std::endl;

    func = [](int x){ return x * 2; };

    std::cout << "bar(5) = " << func(5) << std::endl;
}

Notice we don't mess around with function pointer declarations or library types? Much nicer for everybody to read/write. One thing to notice in this example is the unary + operator; to perform a conversion to function pointer before assigning it to the variable. It actually looks very functional, which seems to be what you're trying to achieve here.

Community
  • 1
  • 1
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
1

Try to unwrap the functor (lambda and std::function) through its operator():

#include <iostream>
#include <functional>

int foo(int bar)
{
    std::cout << bar << std::endl;
    return 0;
}

template<typename /*Fn*/>
struct function_maker;

template<typename RTy, typename... ATy>
struct function_maker<RTy(ATy...)>
{
    template<typename T>
    static std::function<RTy(ATy...)> make_function(T&& fn)
    {
        return std::function<RTy(ATy...)>(std::forward<T>(fn));
    }
};

template<typename /*Fn*/>
struct unwrap;

template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...) const>
    : function_maker<RTy(ATy...)> { };

template<typename CTy, typename RTy, typename... ATy>
struct unwrap<RTy(CTy::*)(ATy...)>
    : function_maker<RTy(ATy...)> { };

template<typename T>
auto func(T f)
    -> decltype(unwrap<decltype(&T::operator())>::make_function(std::declval<T>()))
{
    return unwrap<decltype(&T::operator())>::make_function(std::forward<T>(f));
}

int main()
{
    //auto barp = func(foo); // compilation error
    auto bar = func(std::function<void(int)>(foo));

    auto bar2 = func([](int)
    {
        // ...
    });

    bar(0); // prints 0
}

Demo

Naios
  • 1,513
  • 1
  • 12
  • 26