c++11: How to write a wrapper function to make `std::function` objects
c++, c++11, c++14, std-function
Solution
The problem is your code doesn't handle lambdas, bind, or functionoids properly, your code assumes that all of these take no parameters. To handle these, you'll have to specify the parameter types:
//plain function pointers
template<typename... Args, typename ReturnType>
auto make_function(ReturnType(*p)(Args...))
-> std::function<ReturnType(Args...)>
{return {p};}
//nonconst member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...))
-> std::function<ReturnType(Args...)>
{return {p};}
//const member function pointers
template<typename... Args, typename ReturnType, typename ClassType>
auto make_function(ReturnType(ClassType::*p)(Args...) const)
-> std::function<ReturnType(Args...)>
{return {p};}
//qualified functionoids
template<typename FirstArg, typename... Args, class T>
auto make_function(T&& t)
-> std::function<decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)>
{return {std::forward<T>(t)};}
//unqualified functionoids try to deduce the signature of `T::operator()` and use that.
template<class T>
auto make_function(T&& t)
-> decltype(make_function(&std::remove_reference<T>::type::operator()))
{return {std::forward<T>(t)};}
Variables:
int func(int x, int y, int z) { return x + y + z;}
int overloaded(char x, int y, int z) { return x + y + z;}
int overloaded(int x, int y, int z) { return x + y + z;}
struct functionoid {
int operator()(int x, int y, int z) { return x + y + z;}
};
struct functionoid_overload {
int operator()(int x, int y, int z) { return x + y + z;}
int operator()(char x, int y, int z) { return x + y + z;}
};
int first = 0;
auto lambda = [](int x, int y, int z) { return x + y + z;};
auto lambda_state = [=](int x, int y, int z) { return x + y + z + first;};
auto bound = std::bind(func,_1,_2,_3);
Tests:
std::function<int(int,int,int)> f0 = make_function(func); assert(f0(1,2,3)==6);
std::function<int(char,int,int)> f1 = make_function<char,int,int>(overloaded); assert(f1(1,2,3)==6);
std::function<int(int,int,int)> f2 = make_function<int,int,int>(overloaded); assert(f2(1,2,3)==6);
std::function<int(int,int,int)> f3 = make_function(lambda); assert(f3(1,2,3)==6);
std::function<int(int,int,int)> f4 = make_function(lambda_state); assert(f4(1,2,3)==6);
std::function<int(int,int,int)> f5 = make_function<int,int,int>(bound); assert(f5(1,2,3)==6);
std::function<int(int,int,int)> f6 = make_function(functionoid{}); assert(f6(1,2,3)==6);
std::function<int(int,int,int)> f7 = make_function<int,int,int>(functionoid_overload{}); assert(f7(1,2,3)==6);
std::function<int(char,int,int)> f8 = make_function<char,int,int>(functionoid_overload{}); assert(f8(1,2,3)==6);
http://coliru.stacked-crooked.com/a/a9e0ad2a2da0bf1f The only reason your lambda was succeeding is because it was implicitly convertible to a function pointer because your example doesn't capture any state. Note that my code requires the parameter types for overloaded functions, functionoids with overloaded operator() (including bind), but is now able to deduce all non-overloaded functionoids.
The `decltype` lines are complicated but they're used to deduce the return types. Notice that in NONE of my tests do I need to specify the return type. Let's break down `make_function<short,int,int>` down as if `T` is `char(*)(short, int, int)`:
-> decltype(t(std::declval<FirstArg>(), std::declval<Args>()...))(FirstArg, Args...)
`std::declval<FirstArg>()` is `short{}` (roughly)
-> decltype(t(short{}, std::declval<Args>()...))(FirstArg, Args...)
`std::declval<Args>()...` are `int{}, int{}` (roughly)
-> decltype(t(short{}, int{}, int{})(FirstArg, Args...)
`t(short{}, int{}, int{})` is an `int{}` (roughly)
-> decltype(short{})(FirstArg, Args...)
`decltype(int{})` is `int`
-> int(FirstArg, Args...)
`FirstArg` is still `short`
-> int(short, Args...)
`Args...` are `int, int`
-> int(short, int, int)
So this complex expression merely figures out the function's signature
well, that should look familiar...
Problem
I am trying to write a wrapper `make_function`, which like `std::make_pair` can create a `std::function` object out of suitable callable objects. Just like `make_pair`, for a function pointer `foo`, `auto f0 = make_function(foo);` creates a `std::function` function object `f0` of the right type signature. Just to clarify, I don't mind occasionally giving type parameters to `make_function` in case it is difficult (or impossible) to deduce the type entirely from the parameters. What I came up with so far (code below) works fine for lambdas, some function pointers, and functors (I didn't consider volatiles). But I couldn't get it work for `std::bind` or `std::bind<R>` results. In the code below ``` auto f2 = make_function(std::bind(foo,_1,_2,_3)); //not OK ``` wouldn't compile/work, with gcc 4.8.1. I am guessing that I didn't capture the `operator()` for the `bind` result correctly, but I am not sure how to fix it. Any help on how to fix this case or improvement in other corner cases is appreciated. My question is, of course, how to fix the error in the example below. For background, one of the cases I use this wrapper can be found at this question: How to make C++11 functions taking function<> parameters accept lambdas automatically. If you do not approve the use of `std::function` or my specific way of using it, please leave your comments in that post, and discuss technical issues here. --- EDIT --- From some of the comments, I learned that it's because of the ambiguity issue (ambiguity of the function call operator() of `std::bind` results). As pointed out by @Mooing Duck's answer, the solution is to give the parameter types explicitly. I have updated the code to combine the three functions in @Mooing Duck's answer (with slight change of type parameters), so that the `make_function` wrapper can now handle/type-deduce unambiguous cases as before, and allow specification of complete type signature when there is ambiguity. (My original code for the unambiguous cases is at: https://stackoverflow.com/a/21665705/683218 and can be tested at: https://ideone.com/UhAk91): ``` #include <functional> #include <utility> #include <iostream> #include <functional> using namespace std; // For generic types that are functors, delegate to its 'operator()' template <typename T> struct function_traits : public function_traits<decltype(&T::operator())> {}; // for pointers to member function template <typename ClassType, typename ReturnType, typename... Args> struct function_traits<ReturnType(ClassType::*)(Args...) const> { enum { arity = sizeof...(Args) }; typedef function<ReturnType (Args...)> f_type; }; // for pointers to member function template <typename ClassType, typename ReturnType, typename... Args> struct function_traits<ReturnType(ClassType::*)(Args...) > { enum { arity = sizeof...(Args) }; typedef function<ReturnType (Args...)> f_type; }; // for function pointers template <typename ReturnType, typename... Args> struct function_traits<ReturnType (*)(Args...)> { enum { arity = sizeof...(Args) }; typedef function<ReturnType (Args...)> f_type; }; template <typename L> static typename function_traits<L>::f_type make_function(L l){ return (typename function_traits<L>::f_type)(l); } //handles bind & multiple function call operator()'s template<typename ReturnType, typename... Args, class T> auto make_function(T&& t) -> std::function<decltype(ReturnType(t(std::declval<Args>()...)))(Args...)> {return {std::forward<T>(t)};} //handles explicit overloads template<typename ReturnType, typename... Args> auto make_function(ReturnType(*p)(Args...)) -> std::function<ReturnType(Args...)> { return {p}; } //handles explicit overloads template<typename ReturnType, typename... Args, typename ClassType> auto make_function(ReturnType(ClassType::*p)(Args...)) -> std::function<ReturnType(Args...)> { return {p}; } // testing using namespace std::placeholders; int foo(int x, int y, int z) { return x + y + z;} int foo1(int x, int y, int z) { return x + y + z;} float foo1(int x, int y, float z) { return x + y + z;} int main () { //unambuiguous auto f0 = make_function(foo); auto f1 = make_function([](int x, int y, int z) { return x + y + z;}); cout << make_function([](int x, int y, int z) { return x + y + z;})(1,2,3) << endl; int first = 4; auto lambda_state = [=](int y, int z) { return first + y + z;}; //lambda with states cout << make_function(lambda_state)(1,2) << endl; //ambuiguous cases auto f2 = make_function<int,int,int,int>(std::bind(foo,_1,_2,_3)); //bind results has multiple operator() overloads cout << f2(1,2,3) << endl; auto f3 = make_function<int,int,int,int>(foo1); //overload1 auto f4 = make_function<float,int,int,float>(foo1); //overload2 return 0; } ``` Ideone