0

I need a bind function that behaves like std::bind, but returns an appropriate specialization of std::function.

I think the std::function template arguments can be extracted using the placeholder number from the function's parameter. This doesn't look that trivial though. Is there any implementation available already?


Why I need this

I want to implement a waterfall function with a semantics similar to this JavaScript one.

Here is how I imagine it would look like in C++:

std::function<void(const std::string &)> f = waterfall(
  []( const std::function<void(int)> &cb, const std::string & ) {
    ...;
    cb( 1 );
  },
  []( const std::function<void(double, double)> &cb, int ) {
    ...;
    cb(0.5, 10);
  },
  []( double, double ) {
  }
);

In other words, waterfall would take a bunch of functions, each of which (but the last one) takes a function as the first parameter. waterfall will bind every function to the previous one (starting from the last one, of course), and return a single function.

Basically waterfall should be something like this:

// recursion termination: `waterfall` called with a single functions
template< typename Arg >
auto waterfall( const Arg& first ) -> decltype( first ) {
  return first;
}
// recursion: `waterfall` called with mulitple functions
template< typename Arg, typename... Args >
... waterfall( const Arg& first, Args... args ) {
  return std::bind( first, waterfall(std::forward<Args>(args)...) );
}

There are three open problems though:

  1. Figuring out the return type of waterfall when called with multiple arguments. It cannot be decltype( std::bind(first, waterfall(...)) ) (because C++ doesn't allow recursively calling a template function to deduce its type). If I knew the type of the function (i.e. Arg), that, without the first argument, would be the return type I'm looking for.
  2. I think I need to know the number of arguments of first in order to correctly std::bind it.
  3. std::bind's return type doesn't behave the way I want: nesting std::bind calls merges all the bound functions together, rather than composing them.

I was able to bypass the third point by writing an std::bind wrapper that wraps the bound function into an object of a type T such that std::is_bind_expression<T>::value == false.

For the first two points I need to find out the return type and the arguments type of waterfall parameters. This would be trivial if the functions were std::functions. It would also be simple if they are lambdas, classical functions, or functors with a single operator(): I'd just need to use something like this function_traits. However I'd really like to pass functions bound using std::bind to waterfall, without having to manually cast them into std::functions, because that makes my code way shorter and way clearer with large waterfalls.

Any ideas, thoughts or suggestions?

Community
  • 1
  • 1
peoro
  • 25,562
  • 20
  • 98
  • 150
  • 2
    In C++11, you should prefer using lambda expressions over `std::bind` and friends. Can you show an example for what you want to do? Note that lambda closures are implicitly convertible to `std::function`s. – 5gon12eder Mar 20 '15 at 19:36
  • 1
    Can you explain how you intend to use this? And why you think you need it? In general, you cannot deduce a unique signature for a function call object, let alone one that has some arguments bound. Almost always the consumer of an object knows how it is going to call the object, and as such should be the one determining the type of the erasure, not the supplier of the callable object. The most common half-valid reason is you are writing a remote call protocol (from a different memory space, or a different language) and even there the protocol interface should determine the signature! – Yakk - Adam Nevraumont Mar 20 '15 at 19:38
  • There is one answer here http://stackoverflow.com/a/21788988/683218 (by dyp) to my question about how to return a bind-like object that does not allow superfluous parameters. I don't know if it's relevant or whether you can adapt it to return a std::function object. – thor Mar 20 '15 at 19:40
  • @5gon12eder: Why should I avoid using `std::bind`? `std::bind` is way simpler to type, especially if the bound function (i.e. the lambda) has a bunch of arguments. – peoro Mar 20 '15 at 19:42
  • @Yakk: I need an` std::function` (or something similar -- a lambda would be fine as well, of course), because I need to statically find out the type of its parameter, and -as far as I know- I cannot do this with the object returned by `std::bind`. Using a lambda would work, but I'd rather use a `bind` function, since it's easier to type... – peoro Mar 20 '15 at 19:44
  • @tinlyx: uh, yeah, a `bind` implementation would definitely help to my purpose, thanks. – peoro Mar 20 '15 at 19:45
  • @peoro Why do you need to statically find out the type of its parameter? For what purpose? You cannot do this with a function object in general, let alone the result of a binding: there are hacks cases that will be increasingly narrow in the future. We get questions about this daily on this website: they are almost all for mistaken reasons. `std::function` is not a type of a generic callable, it is a class that lets you type erase details of calling a callable -- if you are deducing the type to type erase to, you are almost always making a mistake. Deduction and type erasure are opposite! – Yakk - Adam Nevraumont Mar 20 '15 at 19:47
  • @Yakk: thanks for the explanation, although I'll need a while to fully understand it. I wanted to write a `waterfall` function, that takes a number of functions all of which (but the last one) have a `std::function` as their first argument. `waterfall` should bind each of them to the previous one (starting from the last one, of course), and return a single function. Is this doable in C++? I believe I'd be able to implement it using a number of `std::function`s. – peoro Mar 20 '15 at 19:54
  • @peoro What do you want to do with arguments, feed them in turn to each of them, like consuming them? Feed the arguments to all of them? Feed all the arguments to the first one? Or will `waterfall` return a nullary (zero-argument) function? – Yakk - Adam Nevraumont Mar 20 '15 at 19:55
  • Or do you want to pass args to each of the waterfalled functions separately, like `waterfall(a,b,c)(1)(2,3)(4.5,6.7)`? – Yakk - Adam Nevraumont Mar 20 '15 at 20:02
  • @Yakk: each function would pass the arguments to the inner one. The result of `waterfall` may have arguments itself. Here an example: `waterfall( []( std::function f ){ ...; f(1); }, []( int ){ ... } )`. Imagine this using more functions, each of which may take more arguments. Basically I'm trying to implement something kind of semantically similar to [this JavaScript function](https://github.com/caolan/async#waterfall). – peoro Mar 20 '15 at 20:02
  • This is a chatroom conversation, not a Q&A. – Lightness Races in Orbit Mar 20 '15 at 20:04
  • `waterfall` would be a simple recursive variadic template function, that simply does something like `return bind( first, waterfall(args...) );`. At least I think/hope this should work. – peoro Mar 20 '15 at 20:06
  • 1
    @LightnessRacesinOrbit: you're right. Should I delete this question, and open a new one asking my final aim (i.e. implementing the `waterfall` thing? – peoro Mar 20 '15 at 20:06
  • 1
    @peoro: Might be wise :) – Lightness Races in Orbit Mar 20 '15 at 20:09
  • [cough](http://coliru.stacked-crooked.com/a/158f0d53e7641f3f) (C++14, but that just takes work to convert to C++11) Do ask the question, and feel free to steal that code and self-answer. If you want to deserve it, convert the C++14 stuff to C++11. ;) – Yakk - Adam Nevraumont Mar 20 '15 at 20:34
  • Edited this answer, rather than starting a new one... The reason is that in the edit I'm just explaining my need, but my real answer is still "how to have a `std::bind` that returns a `std::function`. @Yakk: uh, I know basically nothing about C++14. Anyway thanks a lot for your code, "function return type deduction" looks so sweet! Would that work if the parameters of `waterfall` were the result of a `std::bind`? That's my main goal (and because of that, finding the `waterfall` return type, or writing a `helper` function in C++11 is all but trivial). – peoro Mar 20 '15 at 20:49
  • _"It cannot be `decltype`... because C++ doesn't allow recursively calling a template function to deduce its type."_ You can work around this limitation by doing the recursion in a non-template function of a class template: http://ideone.com/nmnITN. C++14 function return type deduction does not have this limitation: http://ideone.com/BRo5D8. – Oktalist Mar 20 '15 at 21:33

1 Answers1

2

The design here is to do most of the work within a helper class details::waterfall with 2-function waterfall style composition is done in waterfall_call.

#include <iostream>
#include <utility>
#include <functional>
#include <string.h>

namespace details {

This would be slightly more efficient if I decayed outside of the structure definition, as it would reduce unique types in some cases. I don't care:

  template<class F0, class F1>
  struct waterfall_call {
    typename std::decay<F0>::type f0;
    typename std::decay<F1>::type f1;

Call f0 passing it f1 and args...:

    template<class...Args>
    auto operator()(Args&&... args)const
    -> typename std::result_of<F0 const&(F1 const&,Args...)>::type
    {
      return f0( f1, std::forward<Args>(args)... );
    }
  };

The center of the algorithm:

  struct waterfall {

For 2 args, just use waterfall_call:

    template<class F0, class F1>
    waterfall_call<F0, F1> operator()( F0&& f0, F1&& f1 )const
    {
      return { std::forward<F0>(f0), std::forward<F1>(f1) };
    }

For >2 args, recurse, then do a 2 arg solution:

    template<class F0, class...Fs,
      class I=typename std::result_of<waterfall const&(Fs...)>::type
    >
    auto operator()( F0&& f0, Fs&&... fs )const
    -> typename std::result_of< waterfall( F0, I ) >::type
    {
      auto child = (*this)( std::forward<Fs>(fs)... );
      return (*this)( std::forward<F0>(f0), std::move(child) );
    }

For 1 arg, just forward out to a decayed version of the input arg:

    template<class F0>
    auto operator()(F0&&f0)const
    ->typename std::decay<F0>::type
    {
      return std::forward<F0>(f0);
    }
  };
}

And waterfall itself just delegates to details::waterfall:

template<class...Fs>
auto waterfall(Fs&&... fs )
-> typename std::result_of<details::waterfall(Fs...)>::type{
  return details::waterfall{}( std::forward<Fs>(fs)... );
}

live example

The other way (besides the struct trick above) of getting around recursive template return type deduction limitations is to take a parameter by template type from the namespace of the function, and pass it to your recursive calls. This enables ADL lookup, which can recursively find your own function even when deducing your own return type.

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