c++11: How to write a wrapper function to make `std::function` objects

By : tinlyx
Source: Stackoverflow.com
Question!

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: http://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

By : tinlyx


Answers

The big reason why you want to be able to convert lambdas to std::function is because you want two overloads, each taking different signatures.

A good way to solve this involves std::result_of.

Suppose you are making a loop control structure that takes a lambda or other functional. If that functional returns void, you want to loop uncontrolled. If it returns bool or the like, you want to loop while it returns true. If it returns enum ControlFlow, you want to pay attention to the ControlFlow return value (continue or break, say). The function in question takes either the element iterating over, and optionally some extra data (the index in the iteration, maybe some "location" meta-information about the element, etc).

std::result_of would let you pretend to invoke the passed in type with a different number of arguments. A traits class could then figure out which of the above signatures is the "best match", and then route to the implementation that handles that signature (possibly by wrapping the "simpler" cases in a lambda and calling the more complex cases).

Naively, your make_function would could this problem, because you could then simply overload on the various std::function

By : Yakk


This video can help you solving your question :)
By: admin