2

I am studying how to implement simple delegates with c++11. The problem the disconnect-method, I would like to target specific function of specific object to be removed from the map. With this implementation, i erase all functions connected to specific object instance. Id like to be able to do something like :

delegate.disconnect (myobj, &MyObj::method)

instead of

delegate.disconnect (myobj)

which erases all connceted functions for myobj.

template <typename ... Params >
class Delegate {

private:    
    typedef std::function < void (Params...)> FunctionType;
    std::map < void*, std::vector < FunctionType >> m_functions;

public:
    template <typename Class>
    void connect(Class& obj, void (Class::*func) (Params...) ) 
    {
        std::function < void (Class, Params...) >  f = func;
        FunctionType fun = [&obj, f](Params... params) { f(obj, params...); };
        m_functions[&obj].push_back(fun);
    }

    template <typename Class>
    void disconnect(Class& obj) {
        m_functions.erase(&obj);
    }

    template <typename ... Args>
    void operator() (Args...args)
    {       
        for (auto& pair : m_functions) {
            for (auto& func : pair.second) {
                func(args...);
            }
        }
    }
};
KJS
  • 342
  • 1
  • 7
  • std::function only overrides operator== for comparison against nullptr_t. It is therefore not possible to find the std::function again. For this same reason I would rather use a container that does not invalidate iterators upon insertion (in the case of your example - std::map>, and returns to the caller an object (such as Unmapper in my example) that automatically unsubscribes when it goes out of scope (with move semantics to transfer ownership when its scope shall extend that of the caller - the unique_ptr wrap being ideal for this). – Werner Erasmus Nov 27 '13 at 06:30
  • yeah, std::function comparison is not possible. I am hoping to find a way to use the class member function pointer as a key to the map. It's passed as parameter to the connect-method. – KJS Nov 27 '13 at 08:38

2 Answers2

2

I found a way to hash member function which is what i need so i am answering my own question here:

template <typename Class>
size_t getHash(void (Class::*func) (Params...)) {
    const char *ptrptr = static_cast<const char*>(static_cast<const void*>(&func));
    int size = sizeof (func);
    std::string str_rep(ptrptr, size);
    std::hash<std::string> strHasher;
    return strHasher(str_rep);
}

usage is now simple:

delegate.disconnect(&MyClass::MyFunc);

reference: How to hash and compare a pointer-to-member-function?

Community
  • 1
  • 1
KJS
  • 342
  • 1
  • 7
1

std::function is only comparable against nullptr_t see here

For this reason I would use a solution similar to the one provided here below, where the returned object unmaps during destruction by using the iterator. One of course then requires a container who's iterators are not invalidated. A variety of similar idioms can be written, and an attempt can be made at making it entirely generic. I've just provided the idea though (following here below):

As map iterators aren't invalidated when inserting, I usually return an object that wraps the iterator, and unmaps by using the iterator when going out of scope:

It looks something like this...

struct Resource{ virtual ~Resource(){} };

template <class SubjectT, class IterT>
struct Unmapper : Resource
{
  typedef void (SubjectT::*UnmapFunc)( IterT );

  Unmapper(
    std::shared_ptr<SubjectT> subject, UnmapFunc unmap, IterT ref )
  : subject_( subject ), ref_( ref ), unmap_( unmap )
  {
  }

  ~Unmapper()
  {
    std::shared_ptr<SubjectT> subject = subject_.lock();
    if( subject ){ ((*subject).*unmap_)( ref_ ); }
  }
  private:
    std::weak_ptr<SubjectT> subject_;
    IterT ref_;
    UnmapFunc unmap_;

};

template <class SubjectT, class IterT>
std::unique_ptr<Resource> makeUnmapper( std::shared_ptr<SubjectT> subject,
  void (SubjectT::*unmapFunc)( IterT ), IterT ref )
{
  return std::unique_ptr<Resource>( 
    new Unmapper<SubjectT, IterT>( subject, unmapFunc, ref ) );
}

... and ...

//Could generalise by using variadic templates, yes...
class Subject : public std::enable_shared_from_this
{
  public:
    typedef std::function<void()> my_function;
    std::unique_ptr<Resource> subscribe( int id, my_function func )
    {
      //psuedo code...
      my_map::iterator pos = myMap_.insert( id, func );
      if( pos.second )
      {
        return makeUnmapper( shared_from_this(), &Subject::unmap, pos );
      }
      else throw already_subscribed();  
    }

  private:
    //etc... declare map etc...
    void unmap( my_map::iterator pos ){ myMap_.erase( pos ); }
};

Note: I have not compiled this. I might have missed a move, but I doubt it, as the unique_ptr's are always rvalues.

Community
  • 1
  • 1
Werner Erasmus
  • 3,988
  • 17
  • 31
  • Hi! thanks for the reply, this is a really interesting idea though it's not exactly what i was looking for. I might take a stab at this. – KJS Nov 27 '13 at 08:37