Search code examples
c++c++11std-function

Binding a std::function to the same function of a different object instance


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.


Solution

  • 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;
    }