19

std::function<> is a useful wrapper around almost any callable thing, including free functions, lambdas, functors, member functions, results from std::bind. However, when creating a std::function<>, one must explicitly specify the function signature as in (taken from here)

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};

void print_num(int i)
{ std::cout << i << '\n'; }

struct PrintNum {
    void operator()(int i) const
    { std::cout << i << '\n'; }
};

// store a free function
std::function<void(int)> f_display = print_num;

// store a lambda
std::function<void()> f_display_42 = []() { print_num(42); };

// store the result of a call to std::bind
std::function<void()> f_display_31337 = std::bind(print_num, 31337);

// store a call to a member function
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;

// store a call to a member function and object
using std::placeholders::_1;
std::function<void(int)> f_add_display2= std::bind( &Foo::print_add, foo, _1 );

// store a call to a member function and object ptr
std::function<void(int)> f_add_display3= std::bind( &Foo::print_add, &foo, _1 );

// store a call to a function object
std::function<void(int)> f_display_obj = PrintNum();

even though the signature could be inferred from the assigned objects. It seems that a natural way to avoid this (which should be quite handy in heavily templated code) is an overloaded function template make_function (similar in spirit to std::make_pair or std::make_tuple), when above examples would simply become

// store a free function
auto f_display = make_function(print_num);

// store a lambda
auto f_display_42 = make_function([](){ print_num(42);});

// store the result of a call to std::bind
auto f_display_31337 = make_function(std::bind(print_num, 31337));

// store a call to a member function
auto f_add_display = make_function(&Foo::print_add);

// store a call to a member function and object
using std::placeholders::_1;
auto f_add_display2 = make_function(std::bind( &Foo::print_add, foo, _1));

// store a call to a member function and object ptr
auto f_add_display3 = make_function(std::bind( &Foo::print_add, &foo, _1));

// store a call to a function object
auto f_display_obj = make_function(PrintNum());

Another possible use case is to get the return type for callable object of any kind

decltype(make_function(function_object))::return_type;

avoiding the traits magic in the answer by Piotr S. to this question.

So, my question: why does the standard not provide this functionality? Can make_function be implemented without compiler magic? Or would it need compiler magic? (even then the first question remains.)

Community
  • 1
  • 1
Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    Might be because deducing the type of function is really hard (impossible?) in C++. Especially since C++ functions can be (a) templated (b) accept a wide range of types due to implicit conversions. – Rufflewind Jan 07 '15 at 17:53
  • @Rufflewind Are you saying that it is impossible to implement `make_function`? If so, please demonstrate it in an answer. – Walter Jan 07 '15 at 17:55
  • I can't demonstrate it, but I've attempted to deduce the types of a lambda previously and failed. That doesn't necessarily mean it's impossible though, there may be a clever trick that I don't know of. – Rufflewind Jan 07 '15 at 17:57
  • 6
    @Walter Are you saying that it's possible to implement it? That's easily demonstrated---demonstrating that it's impossible may be in itself impossible. – James Kanze Jan 07 '15 at 17:57
  • @JamesKanze I'm *asking* (not *saying*) that. – Walter Jan 07 '15 at 17:59
  • 2
    @Rufflewind Of course, it's possible; this is, after all, the standard library, and the committee could require something that requires compiler magic or things what would otherwise be impossible. (It wouldn't be the first time.) – James Kanze Jan 07 '15 at 17:59
  • 1
    @Walter: Well, the statement in your question: *"even though the signature could be inferred from the assigned objects"* -- Indicates that you take it as a matter of fact that it is possible. – Benjamin Lindley Jan 07 '15 at 18:01
  • @BenjaminLindley I thought so, but am not 100% certain -- I havn't tried to implement it. – Walter Jan 07 '15 at 18:02
  • Of course, the example here is more of an argument for not supporting it; the second version, with all of the `auto` is completely unreadable and unmaintainable. – James Kanze Jan 07 '15 at 18:03
  • @JamesKanze: Right, but the committee has been very conservative about introducing "magical" templates. – Rufflewind Jan 07 '15 at 18:03
  • @Walter: [this](https://stackoverflow.com/a/4482680) may be of interest: it shows that you can deduce the type of a monomorphic lambda (or lambda-like object) but it probably wouldn't be able to handle functions with multiple `operator()` and/or polymorphic ones. – Rufflewind Jan 07 '15 at 18:04
  • @Rufflewind Or other "magic". Agreed. There are at least some influential members of the committee who argue that the standard library should not do anything that user code couldn't do. (More or less: every one accepts that the implelemtation of `std::filebuf` must depend on somethings not available at the C++ language level. But that's not the same sort of "magic".) – James Kanze Jan 07 '15 at 18:05
  • What would be the use case for make_function? make_unique (C++14) and make_shared are used to avoid a raw new call, plus make_shared has an efficiency advantage (one allocation instead of two). – Nevin Jan 07 '15 at 18:11
  • 4
    @Nevin: Reduction in verbosity. Same as the use case for `make_tuple` and `make_pair`. – Benjamin Lindley Jan 07 '15 at 18:14
  • It seems like it wouldn't be that much work to provide an overloaded `make_function(T)` function, one for pointers to functions, one for functor objects (using type traits with `decltype(&T::operator())`, etc.), so that it would work if and only if the `function` type can be unambiguously determined. But I'm having trouble thinking of concrete cases where it could actually be used, and where keeping it as its original possibly-functor type isn't preferable. –  Jan 07 '15 at 18:17
  • @BenjaminLindley I'm still not seeing it. In the case of make_pair/make_tuple, you are converting it to a pair/tuple because you want to do something with it that is unwieldy (such as, say, generate a comparison operator) if you don't hitch the variables together. With functions, about the only thing you can do is call it or copy it; auto is a far better choice than std::function in those cases. – Nevin Jan 07 '15 at 18:22
  • 1
    In the above code, what benefit did you gain from storing anything as a `std::function`? `f_display` and similar are all *less useful* than the original value and value type you created them from. – Yakk - Adam Nevraumont Jan 07 '15 at 18:42
  • Sigh. Now that shamelessly beginning every declaration with `auto` is in vogue, I suppose we're going to need `make_X()` for all `X`. Best get right on that. What could possibly go wrong? – Lightness Races in Orbit Jan 08 '15 at 19:17
  • Quoting Stroustrup (http://www.stroustrup.com/bs_faq.htm): "Standard C++ and the design and programming styles it supports owe a debt to the functional languages, especially to ML. Early variants of ML's type deduction mechanisms were (together with much else) part of the inspiration of templates. Some of the more effective functional programming techniques were part of the inspiration of the STL and the use of function objects in C++." -- can't remember when and where that wisdom is lost among the C++ community. The crippled `std::function` is probably the best example. – thor Mar 18 '15 at 06:02

4 Answers4

15
class multi_functor
{
  public:
    void operator()(int) { std::cout << "i'm int" << std::endl }
    void operator()(double) { std::cout << "i'm double" << std::endl }
};

int main(void)
{
  auto func = make_function(multi_functor());
}

Because what would be the type of func here ?

This ambiguity applies to all functor objects (which includes bind return values and lambdas), which would make make_function only usable on function pointers.

Drax
  • 12,682
  • 7
  • 45
  • 85
  • How does it apply to lambdas? – Walter Jan 07 '15 at 18:17
  • 2
    @Walter `[](auto x){return x;}` – Yakk - Adam Nevraumont Jan 07 '15 at 18:20
  • @Walter Because lambdas juste generates classes with an overloaded `operator()` and some state, see: http://en.cppreference.com/w/cpp/language/lambda – Drax Jan 07 '15 at 18:22
  • @Drax I think the `operator()` (of the closure object) can be *overloaded* only since C++14 (as in Yakk's comment), but not in C++11. – Walter Jan 07 '15 at 18:25
  • Sorry, my wording was bad, i meant defined instead of overloaded :) The point is that, it's an object without a signature in it's type (contrary to function pointers) so it will always be ambiguous. – Drax Jan 07 '15 at 18:26
  • But then, there is no problem with a lambda, isn't there? (except in C++14 a la Yakk's comment) – Walter Jan 07 '15 at 18:27
  • @Walter There is no guarantee that only one overload will exist in lambdas even if that's the case right now. – Drax Jan 07 '15 at 18:31
11

As commented here and elsewhere, there is the ambiguity issue that can confuse type inferencing. Probably these corner cases stopped a std::make_function from being adopted as it would not be able to resolve ambiguity, overloading or work nicely with C++ automatic type conversions. Another argument against it that I see a lot is that std::function has overhead (in type erasure), and many people are against using std::function on that basis for anything other than storage of callables.

However, for the non-ambiguous case, it is possible to write a make_function for lambdas and other callables that takes care of type inferencing, which avoids repeating function type signatures when there is really no ambiguity. One way to do it (taken from my related question) is as follows:

#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;
}
Community
  • 1
  • 1
thor
  • 21,418
  • 31
  • 87
  • 173
3

The general case cannot work. There are specific cases (that support C++11 lambdas but not C++14, does not support bind, supports non-overloaded functions and methods, does not support function objects) where you can build a make_function that "works". There are also some functions you can write that are useful.

The make_function that "works" is usually a bad idea.

Just keep a copy of the original function object around if you don't need to convert it to a std::function<?>. You only need to convert it to a std::function<?> when you already know the types you are going to be passing to it, and what you are doing with the return type -- ie, when you are type-erasing around the signature.

std::function is not a "all purpose holder for a function type". It is a type-erasure class that is used to erase type information so you can have code that operates uniformly on it. If you are deducing the signature from the object, there is little if no reason to store it in a std::function at all.

There are narrow cases where it is useful, when you want to behave differently based on the input and output argument types of the function argument you are passed. In this case, your signature deduction facility could be useful: tying it to std::function would be a bad idea, in my opinion, as it ties together the two independent concepts (signature deduction and type erasure) in a way that is rarely useful.

In short, reconsider.


Now, I mentioned above that there are some useful utilities that could be called make_function. Here are two of them:

template<class...Args, class F>
std::function< std::result_of_t< F&(Args...) >
make_function( F&& f ) {
  return std::forward<F>(f);
}

but requires that you list the arguments. It deduces return value.

This variant:

template<class F>
struct make_function_helper {
  F f;
  template<class...Args>
  std::result_of_t< (F&&)(Args...) >
  operator()(Args&&...args)&& {
    return std::forward<F>(f)(std::forward<Args>(args)...);
  }
  template<class...Args>
  std::result_of_t< (F const&)(Args...) >
  operator()(Args&&...args) const& {
    return f(std::forward<Args>(args)...);
  }
  template<class...Args>
  std::result_of_t< (F&)(Args...) >
  operator()(Args&&...args) & {
    return f(std::forward<Args>(args)...);
  }
  template<class R, class...Args, class=std::enable_if_t<
    std::is_convertible<decltype( std::declval<F&>(Args...) ), R>{}
  >>
  operator std::function<R(Args...)>()&&{ return std::forward<F>(f); }
  template<class R, class...Args, class=std::enable_if_t<
    std::is_convertible<decltype( std::declval<F&>(Args...) ), R>{}
  >>
  operator std::function<R(Args...)>()const&{ return f; }
};
template<class F>
make_function_helper<F> make_function(F&&f) { return {std::forward<F>(f)}; }

doesn't actually make a function, but lets you call a function with multiple std::function overloads and pick between them correctly. It is also invokable in a perfect-forwarding way back to the underlying F. In 97/100 cases, you won't be able to notice the difference between this make_function and one that returns an actual std::function (those last cases being cases where someone expects to type-deduce the std::function from the type, and perfect forwarding failures)

So:

int foo(std::function< int(int) >) { return 0; }
int foo(std::function< void() >) { return 1; }
int main() {
  std::cout << foo( []{} ) << "\n";
}

fails to compile, while

int main() {
  std::cout << foo( make_function([]{}) ) << "\n";
}

succeeds. However, even this trick is just patching a hole in the design of std::function which I hope will be rolled into the post-concepts std::function. At that point, you may have well just store the original object.

In general, you cannot determine a single guaranteed unique signature for a callable object x or a function name x.

In the case of a callable object, the operator() could have multiple overloads. This can be done in C++14 with [](auto x) lambdas, and with function objects or the return from std::bind in C++03.

With the name of a function (or function pointer), the name does not correspond to a single object (or pointer). Resolution is done when it is passed to the std::function, and the correct overload is often picked (because std::function takes a R(*)(Args...) pointer, and maybe something similar for member functions (I cannot recall)).

Doing so with a make_function is nearly impossible.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

Please notice that in all of your examples you can simply remove make_function and you get the same result, or actually more efficient, because invoking an std::function often requires a virtual call. So a first good point would be to discourage the usage of std::function when it is unnecessary.

Typically you use std::function as a member object of some class (callback et similia) or as argument of a function that can not be a template for whatever reason. In both of these cases make_function would be useless.

struct Foo
{
     std::function<int()> callback
};
Foo x; x.callback = [](){return 0;} // No need for make_function

void bar( std::function<int(int)> f );
bar( [](int i){ return i;} ); // No need for make function.

There is only one case I could think of where you can really have a benefit: a std::function initialized by a ternary operator:

 auto f = val ? make_function( foo ) : make_function( bar );

is probably better than

 auto f = val ? std::function<int()>( foo ) : std::function<int()>( bar );

I believe that this is quite rare case, so the advantages of a make_function are minimal.

The real disadvantage, IMO, is that the simple existence of an hypothetical make_function would encourage less experienced developers to use std::function when it is not necessary, as you show in your code.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
  • `... in all of your examples you can simply remove make_function and you get the same result` -- that depends on how you define "same". It's not really the same for most examples such as `auto f_display = make_function(print_num);`, if a `std::function` object in the `auto` variable is intended to be created as in the OP. – thor Jan 11 '15 at 04:02