我一直在尝试用C ++实现类似C#的事件系统,其中tr1函数模板用于存储处理事件的函数。
我创建了一个向量,以便可以将多个侦听器附加到此事件,即:
vector< function<void (int)> > listenerList;
我希望能够从列表中删除处理程序以阻止侦听器接收事件。
那么,如何在此列表中找到与给定侦听器对应的条目?我可以测试列表中的“函数”对象是否指向特定函数吗?
谢谢!
编辑:看过boost :: signal方法,看起来它可能是使用令牌系统实现的,正如你们所建议的那样。 Here's some info on this。观察者在附加到事件时保留“连接”对象,并且此连接对象用于在需要时断开连接。所以看起来你是使用Boost还是使用tr1自己动手,基本原理是一样的。即它会有点笨拙:)答案 0 :(得分:8)
我不知道你是否被锁定在std C ++和tr1中,但如果你不是这样,那么如果你只是使用像boost :: signal和boost :: bind这样的东西,你的问题就好像完全可以避免了解决原始问题 - 创建一个事件系统 - 而不是试图自己动手。
答案 1 :(得分:4)
好的,你让我工作了。困难的部分是试图匹配C#事件的确切使用模式。如果你跳过它,有很多更容易的方法来做你要求的。 (我的同事Jason在整个地方都使用了Notifier对象。)无论如何,这是令人难以置信的无聊代码,可以满足您的需求。不幸的是,它不允许您将参数从Subject传递给Observer。要做到这一点,你需要添加更多的智能。
#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 :(得分:2)
FAQ #1似乎解决了您的问题 - 简单的答案是“不”。
答案 3 :(得分:1)
proposal(第IIIb。节)指出它们无法以任何方式进行比较。如果您向它们附加一些额外信息,则可以轻松识别每个回调。例如,如果您只是定义一个包装函数指针的结构,您可以删除它们(假设您插入了相同的结构)。您还可以向结构添加一些字段(如客户端可以保留的自动生成的guid)并与之进行比较。
答案 4 :(得分:0)
如果您只存储函数指针(而不是其他符合所需签名的仿函数),这很容易(参见下面的代码)。但总的来说,答案就像其他海报所说的那样,不是。在这种情况下,您可能希望将函数存储在哈希值中,作为值,键是用户在添加和删除时提供的内容。
下面的代码演示了如何获取要调用的functor / pointer对象。要使用它,您必须知道要提取的对象的确切类型(即,您指定的类型的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;
}
答案 5 :(得分:0)
怎么样?
map<key-type, function<void (int)> > listeners;
答案 6 :(得分:0)
我有类似的问题,并找到了解决方案。我使用了一些C ++ 0x功能,但只是为了方便,它们不是必不可少的部分。看看这里:
&GT; Messaging system: Callbacks can be anything