Вопрос

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...);
            }
        }
    }
};
Это было полезно?

Решение

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?

Другие советы

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top