3

I am trying to bind a member function to an object while keeping the arguments open. I know I can do it by wrapping it in a lambda expression (like this) but I want to use std::bind and std::placeholders to do it.

So far this is what I've got:

template <size_t _Num>
struct place_holder {};

namespace std {
  template <size_t _Num>
  struct is_placeholder<::place_holder<_Num>> : integral_constant<size_t, _Num> {};
}

namespace _binding_helper_ {
  template <size_t ... _Indices>
  std::tuple<place_holder<_Indices>...> get_placeholders(std::index_sequence<_Indices...>) {
      return std::make_tuple(place_holder<_Indices>()...);
  }
}

template <typename _Obj, typename _Func, typename ... _Args>
std::function<_Func(_Args...)> bind_function_to_object
    (_Func (_Obj::*func)(_Args...), _Obj & obj) {
    return std::bind(func, obj,
        _binding_helper_::get_placeholders(std::make_index_sequence<sizeof...(_Args)>{}));
}

Creating the tuple of place_holders works fine but when I call std::is_placeholder<>::value on the elements of the tuple they are all 0. How can I initialise the place_holder<_Num> struct for all required values of _Num as std::placeholders? Hope there's an easy fix. Any help appreciated. Thanks!

Community
  • 1
  • 1
geanakuch
  • 778
  • 7
  • 13
  • 24

1 Answers1

2

Placeholders are numbered from 1, not from 0. I presume what fails in your static_assert is the condition std::is_placeholder<place_holder<0>>{}, for the reason I mentioned.

Also, you are passing a tuple of placeholders to std::bind. Instead, you should pass the placeholders directly, That is, place_holder<Is + 1>{}....

template <int Num>
struct place_holder {};

namespace std 
{
    template <int Num>
    struct is_placeholder<::place_holder<Num>> : integral_constant<int, Num> {};
}

template <typename Obj, typename C, typename Func, typename... Args, int... Is>
std::function<Func(Args...)> bind_function_to_object(Func(C::*func)(Args...), Obj&& obj, std::integer_sequence<int, Is...>)
{
    return std::bind(func, std::forward<Obj>(obj), place_holder<Is + 1>{}...);
}

template <typename Obj, typename C, typename Func, typename... Args>
std::function<Func(Args...)> bind_function_to_object(Func(C::*func)(Args...), Obj&& obj)
{
    return bind_function_to_object(func, std::forward<Obj>(obj), std::make_integer_sequence<int, sizeof...(Args)>{});
}

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • Brilliant, that did the trick! Thanks a lot! Is there a way to do it without the extra template type C? It seems odd having two template parameters basically describing the same type of object. – geanakuch May 27 '16 at 18:23
  • 1
    @geanakuch it lets you pass a member function of a base class and an instance of a derived class. but if you don't like it, remove perfect forwarding and use an lvalue reference, i.e. `Obj& obj`, then `(Obj::*func)` etc. – Piotr Skotnicki May 27 '16 at 18:30