Сравнение объектов std :: tr1 :: function <> - PullRequest
5 голосов
/ 18 сентября 2008

Я пытался реализовать C # -подобную систему событий в C ++ с помощью шаблонов функций tr1, используемых для хранения функции, которая обрабатывает событие.

Я создал вектор, чтобы к этому событию можно было подключить несколько слушателей, т. Е .:

vector< function<void (int)> >  listenerList;

Я бы хотел иметь возможность удалить обработчик из списка, чтобы слушатель не получал события.

Итак, как мне найти запись в этом списке, которая соответствует данному слушателю? Могу ли я проверить, относится ли объект «функция» в списке к определенной функции?

Спасибо!

РЕДАКТИРОВАТЬ: Изучив подход boost :: signal, кажется, что он, вероятно, реализован с использованием системы токенов, как некоторые из вас предлагали Вот некоторая информация об этом . Наблюдатель сохраняет объект «Соединение», когда он присоединяется к событию, и этот объект соединения используется для отключения при необходимости. Таким образом, похоже, используете ли вы Boost или катите свой собственный с tr1, основной принцип тот же. то есть это будет немного неуклюже :)

Ответы [ 7 ]

8 голосов
/ 18 сентября 2008

Я не знаю, заблокированы ли вы в std C ++ и tr1, но если нет, похоже, вашей проблемы можно полностью избежать, если вы просто используете что-то вроде boost :: signal и boost :: bind решить исходную проблему - создать систему событий - вместо того, чтобы пытаться накатить свою собственную.

4 голосов
/ 18 сентября 2008

Хорошо, ты заставил меня работать. Сложная часть - попытаться найти точный пример использования событий C #. Если вы пропустите это, есть НАМНОГО более простых способов сделать то, что вы просите. (Мой коллега Джейсон повсеместно использует объект Notifier.) В любом случае, вот невероятно скучный код, который делает то, что вы хотите. К сожалению, это не позволяет вам передавать параметры от субъекта к наблюдателю. Для этого вам нужно добавить еще больше умов.

#include "stdafx.h"
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
#include <boost/tr1/functional.hpp>
#include <boost/tr1/memory.hpp>

using namespace std;
using namespace std::tr1;

template <typename T>
class ObserverHandle
{
public:
    typedef boost::function<void (T*)> const UnderlyingFunction;

    ObserverHandle(UnderlyingFunction underlying)
        : _underlying(new UnderlyingFunction(underlying))
    {
    }

    void operator()(T* data) const
    {
        (*_underlying)(data);
    }

    bool operator==(ObserverHandle<T> const& other) const
    {
        return (other._underlying == _underlying);
    }

private:
    shared_ptr<UnderlyingFunction> const _underlying;
};

class BaseDelegate
{
public:
    virtual bool operator==(BaseDelegate const& other)
    {
        return false;
    }

    virtual void operator() () const = 0;
};

template <typename T>
class Delegate : public BaseDelegate
{
public:
    Delegate(T* observer, ObserverHandle<T> handle)
        : _observer(observer),
        _handle(handle)
    {
    }

    virtual bool operator==(BaseDelegate const& other)
    {
        BaseDelegate const * otherPtr = &other;
        Delegate<T> const * otherDT = dynamic_cast<Delegate<T> const *>(otherPtr);
        return ((otherDT) &&
            (otherDT->_observer == _observer) &&
            (otherDT->_handle == _handle));
    }

    virtual void operator() () const
    {
        _handle(_observer);
    }

private:
    T* _observer;
    ObserverHandle<T> _handle;
};

class Event
{
public:
    template <typename T>
    void add(T* observer, ObserverHandle<T> handle)
    {
        _observers.push_back(shared_ptr<BaseDelegate>(new Delegate<T>(observer, handle)));
    }

    template <typename T>
    void remove(T* observer, ObserverHandle<T> handle)
    {
        // I should be able to come up with a bind2nd(equals(dereference(_1))) kind of thing, but I can't figure it out now
        Observers::iterator it = find_if(_observers.begin(), _observers.end(), Compare(Delegate<T>(observer, handle)));
        if (it != _observers.end())
        {
            _observers.erase(it);
        }
    }

    void operator()() const
    {
        for (Observers::const_iterator it = _observers.begin();
            it != _observers.end();
            ++it)
        {
            (*(*it))();
        }
    }

private:
    typedef list<shared_ptr<BaseDelegate>> Observers;
    Observers _observers;

    class Compare
    {
    public:
        Compare(BaseDelegate const& other)
            : _other(other)
        {
        }

        bool operator() (shared_ptr<BaseDelegate> const& other) const
        {
            return (*other) == _other;
        }

    private:
        BaseDelegate const& _other;
    };
};

// Example usage:

class SubjectA
{
public:
    Event event;

    void do_event()
    {
        cout << "doing event" << endl;
        event();
        cout << "done" << endl;
    }
};

class ObserverA
{
public:
    void test(SubjectA& subject)
    {
        subject.do_event();
        cout << endl;

        subject.event.add(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        cout << endl;

        subject.do_event();
        cout << endl;

        subject.event.add(this, _observe);
        subject.event.add(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        subject.do_event();
        subject.event.remove(this, _observe);
        cout << endl;

    }

    void observe()
    {
        cout << "..observed!" << endl;
    }

private:
    static ObserverHandle<ObserverA> _observe;
};

// Here's the trick: make a static object for each method you might want to turn into a Delegate
ObserverHandle<ObserverA> ObserverA::_observe(boost::bind(&ObserverA::observe, _1));

int _tmain(int argc, _TCHAR* argv[])
{
    SubjectA sa;
    ObserverA oa;
    oa.test(sa);

    return 0;
}

А вот и вывод:

делает событие
сделано

делает событие
..observed!
сделано

делает событие
сделано

делает событие
..observed!
..observed!
сделано
выполнение события
..observed!
сделано

2 голосов
/ 18 сентября 2008

FAQ # 1 в документации по функции наддува, кажется, отвечает на ваш вопрос - и простой ответ - "нет".

1 голос
/ 18 сентября 2008

В предложении (раздел IIIb.) Говорится, что они никоим образом не будут сравнимы. Если вы приложите к ним дополнительную информацию, вы можете легко идентифицировать каждый обратный вызов. Например, если вы просто определяете структуру, заключающую в себе указатель на функцию, вы можете удалить их (при условии, что вы вставили ту же структуру). Вы также можете добавить некоторые поля в структуру (например, автоматически сгенерированный guid, за который клиент может держаться) и сравнить с этим.

0 голосов
/ 31 июля 2011

У меня была похожая проблема, и я нашел решение. Я использовал некоторые функции C ++ 0x, но только для удобства, они не являются важной частью. Посмотрите здесь:
> Система обмена сообщениями: обратные вызовы могут быть любыми

0 голосов
/ 18 сентября 2008

А как же

map<key-type, function<void (int)> > listeners;
0 голосов
/ 18 сентября 2008

Если вы храните только указатели функций (а не другие функторы, которые соответствуют требуемой сигнатуре), это легко (см. Код ниже). Но, в общем, ответ, как сказали другие авторы, - нет. В этом случае вы, вероятно, захотите хранить свои функторы в виде хэша в виде значений, а ключи - это то, что пользователь предоставляет при добавлении и удалении.

Код ниже демонстрирует, как получить объект функтор / указатель, который должен быть вызван. Чтобы использовать его, вы должны знать точный тип извлекаемого объекта (т. Е. typeid указанного вами типа должен совпадать с typeid содержащегося функтора / указателя).

#include <cstdio>
#include <functional>

using std::printf;
using std::tr1::function;

int main(int, char**);
static function<int (int, char**)> main_func(&main);

int
main(int argc, char** argv)
{
    printf("%p == %p\n", *main_func.target<int (*)(int, char**)>(), &main);
    return 0;
}
...