13

Is there a way to partially bind the first/last n arguments of a callable object (e.g. function) without explicitly specifying the rest of the arguments?

std::bind() seems to require that all the arguments are be bound, those that are to be left should be bound to std::placeholders::_1,_2,_3 etc.

Is it possible to write a bind_first()/bind_last() for partial binding starting from the first/last argument and that automagically inserts the placeholders for any remaining unbound arguments in their original order in their the original position?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Adi Shavit
  • 16,743
  • 5
  • 67
  • 137
  • There is a `std::bind1st` but its deprecated. – Gasim Jan 19 '14 at 13:00
  • I fear this is impossible to implement – Sebastian Hoffmann Jan 19 '14 at 13:03
  • This is actually pretty trivial to write. All you need to do is store the bound arguments in a `tuple`, accept a variadic number of parameters in `operator()`, unpack the tuple through indices and then append the actual arguments. – Xeo Jan 19 '14 at 13:26
  • I agree with @Xeo - I can't see what is complicated here. I think std::bind wight even do it already. – user3125280 Jan 19 '14 at 13:31
  • 1
    [Here's a rough sketch](http://coliru.stacked-crooked.com/a/cf82f604979d7eaf). The corresponding `postbind` should be pretty easy to write from there. – Xeo Jan 19 '14 at 13:46
  • Yeah templates solves this nicely, oddly bind doesn't. – user3125280 Jan 19 '14 at 13:52
  • @user3125280: Bind is specifically designed that way, to allow for rearrangement and dropping of arguments. – Xeo Jan 19 '14 at 13:53
  • @Xeo could you please eleborate? what is meant by 'dropping of arguments?' I can understand the use of placeholders (rearranging, etc), but not the behaviour when they are missing. – user3125280 Jan 19 '14 at 13:59
  • @user3125280: Sometimes, you simply don't want to accept certain arguments. That way, you can just leave them out, like `std::bind(f, _1, _3)`. This will always drop the second passed argument. Very useful in callbacks where you have no need for all passed arguments. – Xeo Jan 19 '14 at 14:00
  • 3
    FWIW, in C++14 we have generic lambdas, which make the whole thing much easier. [Live example](http://coliru.stacked-crooked.com/a/a23601940f05caff). – Andy Prowl Jan 19 '14 at 14:40
  • @Xeo: In that example, what's the expected type of the second parameter? – Kerrek SB Jan 19 '14 at 15:06
  • @KerrekSB: Unimportant, simply ignored. `f` only has 2 parameters, and the types of those only need to be compatible with the first and third actual argument. – Xeo Jan 19 '14 at 22:04
  • @Xeo: Sorry, I'm a bit slow today. Could you maybe make an example that uses `bind(f, _1, _3)`? – Kerrek SB Jan 19 '14 at 22:27
  • @Kerrek: [Here](http://coliru.stacked-crooked.com/a/d79833e19a093551) – Xeo Jan 19 '14 at 22:50
  • @Xeo: Thanks, I was being stupid in my tests. This is all very interesting. `bind` is a neat thing. – Kerrek SB Jan 19 '14 at 22:59
  • @Xeo: Thanks! You should write up your comments as an answer. – Adi Shavit Jan 20 '14 at 18:26
  • [Here's a prebind](http://ideone.com/UUyhah) with nesting prebind's as well – user3125280 Jan 22 '14 at 10:37
  • 1
    In C++20, there is `std::bind_front`. While in C++23, there is `std::bind_back` – Desmond Gold Jun 19 '22 at 13:48

3 Answers3

6

Neither Boost nor the standard library bind fill in the blanks automatically. You could write such a gadget yourself if you have a rainy evening to fill; here's an example for trailing arguments of a plain function only:

#include <tuple>
#include <type_traits>
#include <utility>

template <typename F, typename ...Args> struct trailing_binder;

template <typename R, typename ...Frgs, typename ...Args>
struct trailing_binder<R(Frgs...), Args...>
{
    template <typename ...Brgs>
    trailing_binder(R (*f)(Frgs...), Brgs &&... brgs)
    : the_function(f)
    , the_args(std::forward<Brgs>(brgs)...)
    { }

    template <unsigned int ...I> struct intlist {};

    template <typename ...Brgs>
    typename std::enable_if<sizeof...(Brgs) + sizeof...(Args) == sizeof...(Frgs), R>::type
    operator()(Brgs &&... brgs)
    {
        return unwrap(std::integral_constant<bool, 0 == sizeof...(Args)>(),
                      intlist<>(),
                      std::forward<Brgs>(brgs)...);
    }

private:
    template <unsigned int ...I, typename ...Brgs>
    R unwrap(std::false_type, intlist<I...>, Brgs &&... brgs)
    {
        return unwrap(std::integral_constant<bool, sizeof...(I) + 1 == sizeof...(Args)>(),
                      intlist<I..., sizeof...(I)>(),
                      std::forward<Brgs>(brgs)...);
    }

    template <unsigned int ...I, typename ...Brgs>
    R unwrap(std::true_type, intlist<I...>, Brgs &&... brgs)
    {
        return the_function(std::get<I>(the_args)..., std::forward<Brgs>(brgs)...);
    }

    R (*the_function)(Frgs...);
    std::tuple<Args...> the_args;
};

template <typename R, typename ...Args, typename ...Frgs>
trailing_binder<R(Frgs...), Args...> trailing_bind(R (*f)(Frgs...), Args &&... args)
{
    return trailing_binder<R(Frgs...), typename std::decay<Args>::type...>(f, std::forward<Args>(args)...);
}

Usage:

int f(int a, int b, int c, int d) { return a + b + c + d; }

int main()
{
    auto b = trailing_bind(f, 1);
    return b(3, 8, 13);
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I recommend you check out my comment-link on the question. :) – Xeo Jan 19 '14 at 13:49
  • @Xeo: You mean the link with all the compiler errors? :-) Bear in mind that the goal is something like `bind(f, 1)` which returns a callable with 3 parameters. – Kerrek SB Jan 19 '14 at 13:49
  • On the *question*, not the other answer. :P – Xeo Jan 19 '14 at 13:50
  • Not using any constructors, really. It's an aggregate. :) (This is actually just laziness on my part. No proper encapsulation that way.) – Xeo Jan 19 '14 at 13:54
0

Inspired by the question, I wrote my own prebind from scratch. It ended up looking pretty similar to everyone else though, but I promise it's original :) - call it convergent evolution.

It has a slightly different flavour though. For one thing, it forwards to it's constructor, but you may prefer to use std::decay (it makes more sense in some ways, but I don't like writing std::ref everywhere). For another I added support for nested prebinds so prebind(foo, prebind(GetRandomNumber))() is the same as prebind(foo)(GetRandomNumber()).

#include <tuple>
#include <type_traits>
using namespace std;

struct pb_tag {}; //use inheritance to mark prebinder structs

//result_of_t will be defined by default in c++1y
template<typename T > using result_of_t = typename result_of<T>::type;
template<typename T> using is_prebinder = is_base_of<pb_tag, typename remove_reference<T>::type >;

//ugly sequence generators for something different
template<int N, int ...S> struct seq : seq<N-1, N, S...> {};
template<int ...S> struct seq<0, S...> {typedef seq type;};

//these three functions are only for nested prebind. they map
//T t -> T t and Prebind<f, T...> -> f(T...)
template<typename T>
auto dispatchee(T&& t, false_type) -> decltype(forward<T>(t)){
    return forward<T>(t);
}

template<typename T>
auto dispatchee(T&& t, true_type) -> decltype(t())
{
    return t();
}

template<typename T>
auto expand(T&& t) -> decltype(dispatchee(forward<T>(t), is_prebinder<T>()))
{
    return dispatchee(forward<T>(t), is_prebinder<T>());
}

template<typename T> using expand_type = decltype(expand(declval<T>()));

//the functor which holds the closure in a tuple
template<typename f, typename ...ltypes>
struct prebinder : public pb_tag
{
    tuple<f, ltypes...> closure;
    typedef typename seq<sizeof...(ltypes)>::type sequence;
    prebinder(f F, ltypes... largs) : closure(F, largs...) {}

    template<int ...S, typename ...rtypes>
    result_of_t<f(expand_type<ltypes>..., rtypes...)>
    apply(seq<0, S...>, rtypes&& ... rargs){
        return (get<0>(closure))(expand(get<S>(closure))... , forward<rtypes>(rargs)...);
    }

    template<typename ...rtypes>
    result_of_t<f(expand_type<ltypes>..., rtypes...)>
    operator() (rtypes&& ... rargs){
        return apply(sequence(), forward<rtypes>(rargs)...);
    }
};

template<typename f, typename ...ltypes>
prebinder<f, ltypes...> prebind(f&& F, ltypes&&... largs)
{
    return prebinder<f, ltypes...>(forward<f>(F), forward<ltypes>(largs)...);
}

It can be easily changed to postbind as well.

Usage looks like:

int g(int a){ return 1 + a; }

int h(){ return 1; }

int i(int a, int b, int c, int d){
    return 1 + a + b + c + d;
}

int main()
{
    //completely bound
    auto a = prebind(g, 1);
    cout << a() << endl;

    //nested bind by reference
    auto b = prebind(g, a);
    cout << b() << endl;
    get<1>(a.closure) = 2;
    cout << b() << endl;

    //bind to prebinder
    auto c = prebind(b);
    cout << c() << endl;

    //nested bind of temp to temp
    auto d = prebind(prebind(g), prebind(h));
    cout << d() << endl;

    //and the one you wanted orginally
    auto e = prebind(i, 1, 1, 1);
    cout << e(1) << endl;

    return 0;
}
user3125280
  • 2,779
  • 1
  • 14
  • 23
0

The advice is now to avoid Bind and use a lambda to wrap a call to your member function.

https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-avoid-bind.html

Dave E
  • 123
  • 1
  • 8