我一直在尝试用tr1函数模板在C ++中实现类似于C#的事件系统,该模板用于存储处理事件的函数。
我创建了一个向量,以便可以将多个侦听器附加到此事件,即:
1
| vector< function<void (int)> > listenerList; |
我希望能够从列表中删除处理程序,以停止侦听器接收事件。
因此,如何在此列表中找到与给定侦听器相对应的条目? 我可以测试列表中的"功能"对象是否引用了特定功能吗?
谢谢!
编辑:研究了boost :: signal方法之后,似乎有些人建议使用令牌系统来实现它。 这是一些信息。 当观察者附加到事件时,他们将保留一个"连接"对象,并且如果需要,可以使用此连接对象断开连接。 这样看来,无论您使用Boost还是将自己的tr1都滚动,基本原理是相同的。 即它将有点笨拙:)
我不知道您是否被锁定在std C ++和tr1中,但是如果您不使用它,则似乎可以完全避免问题,只要您使用boost :: signal和boost :: bind之类的方法来解决您的问题最初的问题-创建事件系统-而不是尝试自己动手做。
好吧,你让我工作了。困难的部分是尝试匹配C#事件的确切用法模式。如果您跳过该步骤,则可以使用许多更简单的方法来完成您要问的事情。 (我的同事Jason到处都使用Notifier对象。)无论如何,这是令人难以置信的代码,它可以满足您的需求。不幸的是,它不允许您将参数从"主题"传递给观察者。为此,您需要添加更多的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
| #include"stdafx.h"
#include <iostream>
#include <string>
#include <list>
#include
#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
boost函数文档中的FAQ#1似乎可以解决您的问题-简单的回答是"否"。
该提案(第IIIb。节)指出,它们在任何方面都是不可比的。如果将一些附加信息附加到它们,则可以轻松识别每个回调。例如,如果仅定义一个包装函数指针的结构,则可以删除它们(假设您插入的结构相同)。您还可以向结构中添加一些字段(例如客户端可以保留的自动生成的GUID)并与之进行比较。
我遇到了类似的问题,并找到了解决方案。我使用了一些C ++ 0x功能,但仅为方便起见,它们并不是必不可少的部分。在这里看看:
>消息系统:回调可以是任何东西
关于什么
1
| map<key-type, function<void (int)> > listeners; |
如果仅存储函数指针(而不存储与所需签名匹配的其他函子),则这很容易(请参见下面的代码)。但是总的来说,就像其他海报所说的那样,答案是否定的。在这种情况下,您可能希望将函子作为值存储在哈希中,而键是用户在添加和删除时提供的键。
下面的代码演示了如何获取要调用的函子/指针对象。要使用它,必须知道要提取的对象的确切类型(即,指定类型的typeid必须与所包含的函子/指针的typeid相匹配)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #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\
", *main_func.target<int (*)(int, char**)>(), &main);
return 0;
} |