реализация делегата c ++, как найти конкретную функцию-член класса

Я изучаю, как реализовать простые делегаты с помощью С ++ 11. Проблема в методе отключения, я хотел бы указать конкретную функцию конкретного объекта, который нужно удалить с карты. В этой реализации я стираю все функции, связанные с конкретным экземпляром объекта. Я хотел бы иметь возможность делать что-то вроде:

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

вместо

delegate.disconnect (myobj)

который стирает все подключенные функции для 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...);
            }
        }
    }
};

person KJS    schedule 26.11.2013    source источник
comment
std :: function только переопределяет operator == для сравнения с nullptr_t. Поэтому снова найти std :: function невозможно. По этой же причине я бы предпочел использовать контейнер, который не делает итераторы недействительными при вставке (в случае вашего примера - std :: map ‹void *, std :: list ‹FunctionType›› и возвращает вызывающей стороне объект ( например, Unmapper в моем примере), который автоматически отменяет подписку, когда выходит за пределы области видимости (с семантикой перемещения для передачи владения, когда ее область действия расширяет область действия вызывающего - обертка unique_ptr идеально подходит для этого).   -  person Werner Erasmus    schedule 27.11.2013
comment
да, сравнение std :: function невозможно. Я надеюсь найти способ использовать указатель на функцию-член класса в качестве ключа к карте. Он передается как параметр в метод подключения.   -  person KJS    schedule 27.11.2013


Ответы (2)


Я нашел способ хеширования функции-члена, что мне и нужно, поэтому я отвечаю на свой вопрос здесь:

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

использование теперь просто:

delegate.disconnect(&MyClass::MyFunc);

ссылка: Как хешировать и сравнивать указатель на функцию-член?

person KJS    schedule 27.11.2013

std :: function можно сравнить только с nullptr_t см. здесь

По этой причине я бы использовал решение, подобное приведенному ниже, где возвращаемый объект отключается во время уничтожения с помощью итератора. Конечно, тогда требуется контейнер, итераторы которого не являются недействительными. Можно написать множество похожих идиом, и можно попытаться сделать их полностью общими. Я только что изложил идею (следуя ниже):

Поскольку итераторы карты не становятся недействительными при вставке, я обычно возвращаю объект, который обертывает итератор, и отключает отображение с помощью итератора при выходе за пределы области видимости:

Выглядит это примерно так ...

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

... и ...

//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 ); }
};

Примечание: я не собирал это. Возможно, я пропустил ход, но я сомневаюсь в этом, поскольку unique_ptr всегда является rvalue.

person Werner Erasmus    schedule 26.11.2013
comment
Привет! спасибо за ответ, это действительно интересная идея, хотя это не совсем то, что я искал. Я мог бы нанести удар по этому поводу. - person KJS; 27.11.2013