Сравнение объектов std :: tr1 :: function <>

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

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

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

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

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

Спасибо!

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
6 958
7

Ответы 7

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

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

Если вы собираетесь использовать структуру с дополнительной информацией, вы также можете сохранить функторы как хеш-значения, а GUID / что-то еще как хеш-ключи. :-)

Chris Jester-Young 18.09.2008 06:55

Функторы нельзя хранить как хеш-значения. С ними ничего не поделать. Обертывание их в структуру (даже одноэлементную структуру, состоящую только из функтора) позволит вам делать с ними все, что угодно. Умышленно, что их нельзя сравнивать / хешировать / т. Д. Без некоторой работы.

hazzen 18.09.2008 20:32

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

В приведенном ниже коде показано, как получить вызываемый объект-функтор / указатель. Чтобы использовать его, вы должны знать точный тип извлекаемого объекта (т.е. 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;
}

Как насчет

map<key-type, function<void (int)> > listeners;

Я думаю, что хеш (std :: tr1 :: unordered_map) - лучший выбор для такого рода вещей. Если только не важно вызывать слушателей в порядке регистрации, в этом случае тип ключа должен включать / быть серийным номером.

Chris Jester-Young 18.09.2008 10:58

Хорошо, ты заставил меня работать. Самая сложная часть - это попытка соответствовать точному шаблону использования событий 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;
}

И вот результат:

doing event
done

doing event
..observed!
done

doing event
done

doing event
..observed!
..observed!
done
doing event
..observed!
done

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

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

Chris Jester-Young 18.09.2008 10:59

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

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