5

Is it possible to rebind a std::function to point to the same function but with a different object instance?

Say if I have an object that has a std::function that is bound to another function, but if that object was copied to another instance, I'd like to rebind the std::function to that new instance instead of the old instance.

#include "stdafx.h"
#include <iostream>
#include <functional>

class EventHandler
{
public:
    int Num;
    std::function<int()> OnEvent;

    EventHandler (int inNum)
    {
        Num = inNum;
    }

    EventHandler (const EventHandler& other)
    {
        Num = other.Num;
        OnEvent = other.OnEvent; //TODO:  Need some way to redirect the std::function to the new instance rather than having the delegate point to the original object's handler.
    }

    int HandleEvent ()
    {
        return Num;
    }
};

int main()
{
    EventHandler a(4);
    a.OnEvent = std::bind(&EventHandler::HandleEvent, a);
    EventHandler b(a);
    b.Num = 5;
    //Uncommenting the line below is a manual way of redirecting event handler to the new instance.
    //b.OnEvent = std::bind(&EventHandler::HandleEvent, b);

    int aResult = a.OnEvent();
    int bResult = b.OnEvent();

    //This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
    std::cout << "aResult=" << aResult << "  bResult=" << bResult << '\n';

    return 0;
}

I'm open to having a wrapper of the std::function to store additional information.

DW_Ant
  • 61
  • 7
  • is the question how to do this thing specifically, or is it how to get this concept to work in code? What I mean is, there are other ways to get the EventHandler setup correctly. For example, the constructors could just std::bind(&EventHandler::HandleEvent, this);. There are also other options, but I wasn't clear on what you were aiming for. – tmruss Jun 08 '16 at 05:18
  • It's more about how to get this concept to work in code. I think I may have oversimplified the example for clarity. The actual code base I'm working with has parent-child relationship with objects. When a parent is copied, its children is also copied. I need to migrate the children's std::functions to point to the new instances instead of pointing to the originals. – DW_Ant Jun 08 '16 at 05:29
  • Let me preface this by saying I would probably try to find a different way to organize things then to solve this with what I'm about to say. It sounds like a design-y problem that might have a simpler solution. however, you can make a template wrapper that stores a pointer-to-member that will be bound in the function. Then, in your assignment operator you bind using the pointer-to-member as the target and 'this' as the instance. the only reason to do this is if you are trying to make it generic. if it's always the same function I would just re-bind it. – tmruss Jun 08 '16 at 05:38
  • What I mean by a simpler solution is: you could have the child objects have a 'set callback' function or "initialize parent" or something like that. When you do the assignment, you copy over all the child objects and tell them to set their parent to the new instance. Or something like that. Let me know more details if you want, we can talk about it more. – tmruss Jun 08 '16 at 05:43
  • Don't you simply want a pointer-to-member-function? – JohnB Jun 08 '16 at 09:01
  • It's important to realize that `std::function` isn't used just with `std::bind`. For instance, `std::function` will also take an ordinary `X(*)(Y)` function pointer. Clearly you can't rebind something that wasn't bound in the first place. The whole point of `std::function` is to do this type erasure, but it appears you don't want type erasure at all! – MSalters Jun 08 '16 at 09:52

4 Answers4

1

What your event handler does should depend on which instance it is called on. Hence, logically, the correct way of solving the problem is providing the instance as a parameter to the handler function, e.g.

#include <iostream>
#include <functional>

class EventHandler
{
private:
  std::function<int(EventHandler &)> handlingFunction;

public:
  int Num;

  EventHandler (int inNum)
  : handlingFunction ([] (EventHandler &) -> int { throw 0; })
  , Num (inNum)
  { }

  void SetHandlingFunction (std::function<int(EventHandler &)> f) {
     handlingFunction = f;
  }

  // for convenience, if the handling function is a member
  void SetHandlingFunction (int EventHandler::*mf ()) {
      handlingFunction = 
         [mf] (EventHandler & myself) -> int { return myself.*mf (); }
      ;
  }

  int OnEvent () {
     return handlingFunction (*this);
  }

  int HandleEvent ()
  {
     return Num;
  }
};

int main()
{
   EventHandler a(4);
   a.SetHandlingFunction ( [] (EventHandler & h) -> int { return h.HandleEvent (); } );

   // or
   a.SetHandlingFunction (&EventHandler::HandleEvent);

   EventHandler b(a);
   b.Num = 5;

   int aResult = a.OnEvent();
   int bResult = b.OnEvent();

   std::cout << "aResult=" << aResult << "  bResult=" << bResult << '\n';

   return 0;
}

Of course, if your handling function always is a member function, you can simply replace the std::function by a pointer-to-member-function.

Note that you should properly initialize the handlingFunction member in the constructor of your EventHandler class, e.g. by setting it to a dummy function.

JohnB
  • 13,315
  • 4
  • 38
  • 65
1

The following code introduced a binding_function<R(Args...)>, which is called like function<R()>, and arguments can be rebind anytime after it constructed (assuming it was not nullptr).

#include <functional>
#include <tuple>
#include <utility>
#include <memory>
#include <iostream>

template <typename T>
class binding_function;

template <typename R, typename... Args>
class binding_function<R(Args...)> : std::function<R()>
{
  using base_function = std::function<R(Args...)>;
  using binded_function = std::function<R()>;
  base_function base;

public:
  binding_function() = default;

  template <typename BaseF, typename... TArgs>
  binding_function(BaseF&& f, TArgs&&... args)
    : base(std::forward<BaseF>(f)) {
    rebind(std::forward<TArgs>(args)...);
  }

  template <typename... TArgs>
  void rebind(TArgs&&... args)
  {
    static_cast<binded_function&>(*this) = 
      std::bind(base, std::forward<TArgs>(args)...);
  }

  using binded_function::operator();
};

class EventHandler
{
public:
    // change type of OnEvent to binding_function
    binding_function<int(EventHandler)> OnEvent;

    // others remain the same
};

int main()
{
    EventHandler a(4);

                // first binding
    a.OnEvent = {&EventHandler::HandleEvent, a};
    EventHandler b(a);
    b.Num = 5;
    b.OnEvent.rebind(b);  // rebinding

    int aResult = a.OnEvent();
    int bResult = b.OnEvent();

    //This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
    std::cout << "aResult=" << aResult << "  bResult=" << bResult << '\n';

    return 0;
}
user1887915
  • 1,299
  • 10
  • 13
  • I still have much to learn. I didn't know about std::Forward and 'using' keyword. I don't completely understand the first class definition 'binding_function', but it's needed for some reason. I'm still studying this code until I get the aha moment. This code does work wonderfully though! – DW_Ant Jun 11 '16 at 18:49
  • @DW_Ant [`using` in class definition](http://en.cppreference.com/w/cpp/language/using_declaration#In_class_definition) [`using` type alias](http://en.cppreference.com/w/cpp/language/type_alias) [`std::forward`](http://en.cppreference.com/w/cpp/utility/forward) – user1887915 Jun 12 '16 at 08:47
  • How would this look like if the function in question has arguments it self? – veio Nov 22 '16 at 17:52
1

I extended user1887915's answer to allow functions with parameters:

#include <functional>
#include <tuple>
#include <utility>
#include <memory>
#include <iostream>

template <typename T>
class binding_function;

template <typename R, typename... Args, typename SelfType>
class binding_function<R(SelfType, Args...)> : std::function<R(Args...)>
{
  using base_function = std::function<R(SelfType, Args...)>;
  using binded_function = std::function<R(Args...)>;
  base_function base;

public:
  binding_function() = default;

  template <typename BaseF, typename... TArgs>
  binding_function(BaseF&& f, SelfType t, TArgs&&... args)
    : base(std::forward<BaseF>(f)) {
    rebind(std::forward<SelfType>(t), std::forward<TArgs>(args)...);
  }

  template <typename T, typename... TArgs>
  void rebind(T&& t, TArgs&&... args)
  {
    static_cast<binded_function&>(*this) =
      std::bind(base, std::forward<SelfType>(t), std::forward<TArgs>(args)...);
  }

  using binded_function::operator();
};



class EventHandler
{
public:
    int Num;
    binding_function<int(EventHandler, int)> OnEvent;


    EventHandler (int inNum)
    {
        Num = inNum;
    }

    EventHandler (const EventHandler& other)
    {
        Num = other.Num;
        OnEvent = other.OnEvent; //TODO:  Need some way to redirect the std::function to the new instance rather than having the delegate point to the original object's handler.
    }
    int HandleEvent (int value)
    {
        return Num + value;
    }
};
int main()
{
    EventHandler a(4);

                // first binding
    a.OnEvent = {&EventHandler::HandleEvent, a, std::placeholders::_1};
    EventHandler b(a);
    b.Num = 5;
    b.OnEvent.rebind(b, std::placeholders::_1);  // rebinding

    int aResult = a.OnEvent(1);
    int bResult = b.OnEvent(1);

    //This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
    std::cout << "aResult=" << aResult << "  bResult=" << bResult << '\n';

    return 0;
}
veio
  • 507
  • 4
  • 16
  • If I call rebind in the copy constructor, the bind function calls the copy constructor again, which leads to infinite recursion. Does anybody know how to fix that? – veio Nov 22 '16 at 19:31
0

AFAIK what you are asking is not possible, but I think there is a workaround that you can do:

class EventHandler
{
public:
    int Num;
    std::function<int()> OnEvent;

    template <typename Func>
    EventHandler (int inNum, Func on_event)
    {
        Num = inNum;
        OnEvent = [=]() { return (this->*on_event)(); };
    }

    EventHandler (const EventHandler& other): EventHandler(other.Num, &EventHandler::HandleEvent) {}

    int HandleEvent ()
    {
        return Num;
    }
};

int main()
{
    EventHandler a(4, &EventHandler::HandleEvent);
    EventHandler b(a);
    b.Num = 5;

    int aResult = a.OnEvent();
    int bResult = b.OnEvent();

    //This will print out 4 and 4 instead of 4 and 5 since b is still bound to a's event handler.
    std::cout << "aResult=" << aResult << "  bResult=" << bResult << '\n';

    return 0;
}

This prints "aResult=4 bResult=5" as you want. Also, I think by employing a bit more metaprogramming magic, we can try to prettify the syntax.

Let me know if this works for you.

Arunmu
  • 6,837
  • 1
  • 24
  • 46
  • Also, if its a requirement to be able to point to regular functions, it should be possible as well with a bit of help using tag dispatching. – Arunmu Jun 08 '16 at 05:49