比较std :: tr1 :: function <>对象
我一直在尝试使用tr1函数模板(用于存储处理事件的函数)在C ++中实现类似于C#的事件系统。
我创建了一个向量,以便可以将多个侦听器添加到此事件,即:
vector< function<void (int)> > listenerList;
我希望能够从列表中删除处理程序,以停止侦听器接收事件。
因此,如何在此列表中找到与给定侦听器相对应的条目?我可以测试列表中的"功能"对象是否引用了特定功能吗?
谢谢!
编辑:研究了boost :: signal方法之后,似乎有些人建议使用令牌系统来实现它。这是一些信息。当观察者添加到事件时,观察者将保留"连接"对象,并且如果需要,可以使用此连接对象断开连接。因此,看起来我们是使用Boost还是将自己的tr1滚动使用,基本原理是相同的。即它将有点笨拙:)
解决方案
boost函数文档中的FAQ#1似乎可以解决问题,简单的回答是"否"。
该提案(第IIIb。节)指出,它们在任何方面都是不可比的。如果将一些添加信息添加到它们,则可以轻松识别每个回调。例如,如果仅定义一个包装函数指针的结构,则可以删除它们(假设我们插入的结构相同)。我们还可以向该结构添加一些字段(例如客户端可以保留的自动生成的GUID)并与之进行比较。
如果仅存储函数指针(而不存储与所需签名匹配的其他函子),则这很容易(请参见下面的代码)。但是总的来说,就像其他海报所说的那样,答案是否定的。在这种情况下,我们可能希望将函子作为值存储在哈希中,而键是用户在添加和删除时提供的键。
下面的代码演示了如何获取要调用的函子/指针对象。要使用它,我们必须知道要提取的对象的确切类型(即,指定类型的" 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;
好吧,你让我工作了。困难的部分是尝试匹配Cevents的确切使用模式。如果我们跳过该步骤,则可以使用许多更简单的方法来完成我们要问的事情。 (我的同事Jason到处都使用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之类的方法来解决问题创建事件系统的原始问题,而不是尝试自己动手做。