2

Recently, I've frequently been binding member functions to instances of objects. Rather than using a combination of std::bind, std::mem_fn and std::ref, I'd like to combine all of these into one function to handle it all automatically.

For example, consider the following code:

#include <functional>
#include <iostream>
#include <string>

// This is the function I'd like to have working:
template <class T, typename RETURN_TYPE, typename... Arguments>
std::function<RETURN_TYPE(Arguments...)> obj_bind(RETURN_TYPE (T::*in_fun)(), T & obj, Arguments... params)
{
  return std::bind( std::mem_fn(in_fun), std::ref(obj), params...);
}

int main()
{
  // Standard use of push_back member function (our test case):
  std::string test_obj = "Test 1: ";
  test_obj.push_back('A');
  std::cout << test_obj << std::endl;

  // This WORKS:
  auto test_fun = std::bind( std::mem_fn(&std::string::push_back), std::ref(test_obj), 'B');

  // But I'd like to use this instead:
  // auto test_fun = obj_bind( &std::string::push_back, test_obj, 'C');

  test_obj = "Test 2: ";
  test_fun();
  std::cout << test_obj << std::endl;
}

My obj_bind function actually works fine on member functions with no arguments, so I'm pretty sure my problem lies in how I'm handling those, but after several failed attempts to fix it, I thought I'd try here for advice.

Charles Ofria
  • 1,936
  • 12
  • 24
  • 3
    Mabye I am missing something but with `std::bind` you don't need `std::mem_fn`. – Stephan Dollberg Dec 07 '14 at 21:04
  • 1
    I can confirm that I just removed the mem_fn and it works fine on g++. Maybe you're using a broken/old library? – Giulio Franco Dec 07 '14 at 21:07
  • I don't understand. How is `obj_bind` appreciably different from just `std::bind()`? You can just do `std::bind(&string::push_back, test_obj, 'C')`, no? – Barry Dec 07 '14 at 21:15
  • Ah -- I had somehow missed that `std::bind` did not require `std::mem_fn`. That will do a lot of simplification for me. As for differences from `std::bind`: the `std::ref` is still needed to bind to a reference of the target object. – Charles Ofria Dec 07 '14 at 21:21
  • `std::bind(&string::push_back, &test_obj, 'C')` would be a closer analogue, using `&test_obj` is equivalent to `std::ref(test_obj)` for this purpose, it causes `bind` to refer to the original object rather than copy it – Jonathan Wakely Dec 08 '14 at 11:34

1 Answers1

2

Before diving into fixing your binder, a few notes:

  1. You don't really need to use std::mem_fn() for the first argument of std::bind() as std::bind() already knows how to deal with member function pointers.
  2. The type of taking an address of a member function is undefined. That is, you can't really on std::string::push_back being a member function pointer taking just one argument.
  3. Instead of using std::ref(testobj) you could jut juse &testobj.

You can't deduce a member function like this if it needs to have argument: you'll need to specify the argument:

template <class T, typename RETURN_TYPE, typename...Args, typename... Arguments>
std::function<RETURN_TYPE(Arguments...)>
obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params) {
    ...
}

This gets you over the immediate error. The next problem is that you somehow related the bound arguments to the type of function being called. Of course, that's not how it works. In your case, the resulting argument actually doesn't take any arguments, i.e., you'd have a declaration like this:

template <class T, typename RETURN_TYPE, typename... Args, typename... Arguments>
std::function<RETURN_TYPE()>
obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params) {
    ...
}

In case you actually use a placeholder with your function, the returned std::function<RC(...)> would actually take some argument. Figuring these arguments out is somewhat non-trivial. When restricting the argument type to a pointer to function or a pointer to member function it should be doable to return an appropriate function object, though.

Just for the fun of it, here is an implementation which seems to deal with placeholders (it isn't thoroughly tested, though):

#include <functional>
#include <iostream>
#include <string>

using namespace std::placeholders;

struct foo
{
    void f(int i, double d, char c) {
        std::cout << "f(int=" << i << ", double=" << d << ", char=" << c << ")\n";
    }
};

template <typename...> struct type_list;

template <typename, typename, typename, typename> struct objbind_result_type;
template <typename RC, typename... RA>
struct objbind_result_type<RC, type_list<RA...>, type_list<>, type_list<> > {
    typedef std::function<RC(RA...)> type;
};

template <typename RC,
          typename... RA,
          typename A0, typename... A,
          typename B0, typename... B>
struct objbind_result_type<RC, type_list<RA...>,
                           type_list<A0, A...>,
                           type_list<B0, B...> >;

template <bool, typename, typename, typename, typename, typename>
struct objbind_result_type_helper;
template <typename A0, typename RC, typename... RA, typename... A, typename...B>
struct objbind_result_type_helper<true, A0, RC, type_list<RA...>, type_list<A...>, type_list<B...> > {
    typedef typename objbind_result_type<RC, type_list<RA..., A0>, type_list<A...>, type_list<B...> >::type type;
};

template <typename A0, typename RC, typename... RA, typename... A, typename...B>
struct objbind_result_type_helper<false, A0, RC, type_list<RA...>, type_list<A...>, type_list<B...> > {
    typedef typename objbind_result_type<RC, type_list<RA...>, type_list<A...>, type_list<B...> >::type type;
};

template <typename RC,
          typename... RA,
          typename A0, typename... A,
          typename B0, typename... B>
struct objbind_result_type<RC, type_list<RA...>,
                           type_list<A0, A...>,
                           type_list<B0, B...> > {
    typedef typename objbind_result_type_helper<bool(std::is_placeholder<B0>::value), A0,
                                                RC, type_list<RA...>,
                                                type_list<A...>,
                                                type_list<B...> >::type type;
};


// This is the function I'd like to have working:
template <class T, typename RETURN_TYPE, typename...Args, typename... Arguments>
typename objbind_result_type<RETURN_TYPE, type_list<>, type_list<Args...>, type_list<Arguments...> >::type
obj_bind(RETURN_TYPE (T::*in_fun)(Args...), T & obj, Arguments... params)
{
  return std::bind(in_fun, &obj, params...);
}

int main()
{
  foo fo;
  auto fun = obj_bind(&foo::f, fo, _1, 2.34, _2);
  fun(17, 'b');
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    I see. I knew the definition for `std::bind` would need to be complex, but given how hard it is to build something that just uses bind dynamically, it must be much worse than I was expecting. However, given that bind actually does what I need with just the addition of an &, I clearly don't need this new function. I really appreciate all of the help. – Charles Ofria Dec 07 '14 at 21:39
  • 1
    Minor clarification: The type of taking an address of a member function _of a standard library type_ is undefined. It's probably fine for user-defined types in most cases. – Jonathan Wakely Dec 08 '14 at 11:32