POCO C++库学习和分析 -- 通知和事件 (四)
POCO C++库学习和分析 -- 通知和事件 (四)
5. 事件
Poco中的事件和代理概念来自于C#。对于事件的使用者,也就是调用方来说,用法非常的简单。5.1 从例子说起
首先让我们来看一个同步事件例子,然后再继续我们的讨论:#include "Poco/BasicEvent.h" #include "Poco/Delegate.h" #include <iostream> using Poco::BasicEvent; using Poco::Delegate; class Source { public: BasicEvent<int> theEvent; void fireEvent(int n) { theEvent(this, n); // theEvent.notify(this, n); // alternative syntax } }; class Target { public: void onEvent(const void* pSender, int& arg) { std::cout << "onEvent: " << arg << std::endl; } }; int main(int argc, char** argv) { Source source; Target target; source.theEvent += Poco::delegate(&target, &Target::onEvent); source.fireEvent(42); source.theEvent -= Poco::delegate(&target, &Target::onEvent); return 0; }
从上面的代码里,我们可以清晰的看到几个部分,数据源Source,事件BasicEvent<T>,目标对象Target。
其中source.theEvent += Poco::delegate(&target, &Target::onEvent)完成了,目标向数据源事件注册的过程。大家都知道在C++中,程序运行是落实到类的实例的,看一下消息传递的过程,Poco是如何解决这个问题。target是目标对象实例,Target::onEvent目标对象处理事件的函数入口地址。source.fireEvent(42)触发事件运行,其定义为:
void fireEvent(int n) { theEvent(this, n); // theEvent.notify(this, n); // alternative syntax }theEvent(this, n)中存在两个参数,其中n为Target::onEvent(const void* pSender, int& arg)处理函数的参数,可理解为消息或者事件内容;this给出了触发源实例的信息。
ok。这样消息的传递流程出来了。消息源实例的地址,消息内容,目标实例地址,目标实例类的处理函数入口地址。使用者填入上述信息就可以传递消息了。相当简单。
而对于事件的开发者,如何实现上述功能。这是另外一码事,用C++实现这么一个功能还是挺复杂的一件事。看一下使用语言的方式,想一下用到的C++技术:
1. +=/-= 重载
source.theEvent += Poco::delegate(&target, &Target::onEvent);2. 仿函式
theEvent(this, n);3. 模板
开发者是不应该限定使用者发送消息的类以及接受消息类的类型的,因此C++中能够完成此功能的技术只有模板了。关于模板编程还想聊上几句。STL的特点在于算法和数据结构的分离,这个其实也是泛型编程的特点。如果把使用者对于类的应用过程看做算法过程的话,就可以对这个过程进行泛型编程。同时应该注意的是,算法和数据结构是存在关联的,这是隐含在泛型编程中的,能够使用某种算法的数据结构一定是符合该种算法要求的。
就拿Poco中事件的委托Delegate来说,目标对象处理事件的函数入口是存在某种假设的。Poco中假设入口函数必须是如下形式之一:
void (TObj::*NotifyMethod)(const void*, TArgs&); void (TObj::*NotifyMethod)(TArgs&); void (*NotifyMethod)(const void*, TArgs&); void (*NotifyMethod)(void*, TArgs&);
5.2 事件的实现
下面一张图是Poco中Event的类图:下面另一张图是Poco中Event流动的过程:
从图上看实现事件的类被分成了几类:
1) Delegate:
AbstractDelegate,Delegate,Expire,FunctionDelegate,AbstractPriorityDelegate,PriorityDelegate,FunctionPriorityDelegate:
2) Strategy:
NotificationStrategy,PriorityStrategy,DefaultStrategy,FIFOStrategy
3) Event:
AbstractEvent,PriorityEvent,FIFOEvent,BasicEvent
我们取Delegate,DefaultStrategy,BasicEvent来分析,其他的只是在它们的基础上加了一些修饰,流程类似。
Delegate类定义如下:
template <class TObj, class TArgs, bool withSender = true> class Delegate: public AbstractDelegate<TArgs> { public: typedef void (TObj::*NotifyMethod)(const void*, TArgs&); Delegate(TObj* obj, NotifyMethod method): _receiverObject(obj), _receiverMethod(method) { } Delegate(const Delegate& delegate): AbstractDelegate<TArgs>(delegate), _receiverObject(delegate._receiverObject), _receiverMethod(delegate._receiverMethod) { } ~Delegate() { } Delegate& operator = (const Delegate& delegate) { if (&delegate != this) { this->_receiverObject = delegate._receiverObject; this->_receiverMethod = delegate._receiverMethod; } return *this; } bool notify(const void* sender, TArgs& arguments) { Mutex::ScopedLock lock(_mutex); if (_receiverObject) { (_receiverObject->*_receiverMethod)(sender, arguments); return true; } else return false; } bool equals(const AbstractDelegate<TArgs>& other) const { const Delegate* pOtherDelegate = reinterpret_cast<const Delegate*>(other.unwrap()); return pOtherDelegate && _receiverObject == pOtherDelegate->_receiverObject && _receiverMethod == pOtherDelegate->_receiverMethod; } AbstractDelegate<TArgs>* clone() const { return new Delegate(*this); } void disable() { Mutex::ScopedLock lock(_mutex); _receiverObject = 0; } protected: TObj* _receiverObject; NotifyMethod _receiverMethod; Mutex _mutex; private: Delegate(); };
我们可以看到Delegate类中存储了目标类实例的指针_receiverObject,同时存储了目标类处理函数的入口地址_receiverMethod,当初始化Delegate实例时,参数被带进。
Delegate类中处理事件的函数为bool notify(const void* sender, TArgs& arguments),这是一个虚函数. 如果去看它的实现的话,它最终调用了目标类处理函数
(_receiverObject->*_receiverMethod)(sender, arguments)。如果用简单的话来描述Delegate的作用,那就是目标类的代理。
在Poco中对于Delegate提供了模板函数delegate,来隐藏Delegate对象的创建,其定义如下:
template <class TObj, class TArgs> static Delegate<TObj, TArgs, true> delegate(TObj* pObj, void (TObj::*NotifyMethod)(const void*, TArgs&)) { return Delegate<TObj, TArgs, true>(pObj, NotifyMethod); }
在来看DefaultStrategy类,其定义如下:
template <class TArgs, class TDelegate> class DefaultStrategy: public NotificationStrategy<TArgs, TDelegate> /// Default notification strategy. /// /// Internally, a std::vector<> is used to store /// delegate objects. Delegates are invoked in the /// order in which they have been registered. { public: typedef SharedPtr<TDelegate> DelegatePtr; typedef std::vector<DelegatePtr> Delegates; typedef typename Delegates::iterator Iterator; public: DefaultStrategy() { } DefaultStrategy(const DefaultStrategy& s): _delegates(s._delegates) { } ~DefaultStrategy() { } void notify(const void* sender, TArgs& arguments) { for (Iterator it = _delegates.begin(); it != _delegates.end(); ++it) { (*it)->notify(sender, arguments); } } void add(const TDelegate& delegate) { _delegates.push_back(DelegatePtr(static_cast<TDelegate*>(delegate.clone()))); } void remove(const TDelegate& delegate) { for (Iterator it = _delegates.begin(); it != _delegates.end(); ++it) { if (delegate.equals(**it)) { (*it)->disable(); _delegates.erase(it); return; } } } DefaultStrategy& operator = (const DefaultStrategy& s) { if (this != &s) { _delegates = s._delegates; } return *this; } void clear() { for (Iterator it = _delegates.begin(); it != _delegates.end(); ++it) { (*it)->disable(); } _delegates.clear(); } bool empty() const { return _delegates.empty(); } protected: Delegates _delegates; };
哦,明白了,DefaultStrategy是一组委托的集合,内部存在的_delegates定义如下:
std::vector<SharedPtr<TDelegate>> _delegateDefaultStrategy可以被理解成一组目标的代理。在DefaultStrategy的notify函数中,我们可以设定,当一个事件发生,要送给多个目标时,所采取的策略。NotificationStrategy,PriorityStrategy,DefaultStrategy,FIFOStrategy之间的区别也就在于此。
最后来看一下BasicEvent类。它的定义是:
template <class TArgs, class TMutex = FastMutex> class BasicEvent: public AbstractEvent < TArgs, DefaultStrategy<TArgs, AbstractDelegate<TArgs> >, AbstractDelegate<TArgs>, TMutex > /// A BasicEvent uses the DefaultStrategy which /// invokes delegates in the order they have been registered. /// /// Please see the AbstractEvent class template documentation /// for more information. { public: BasicEvent() { } ~BasicEvent() { } private: BasicEvent(const BasicEvent& e); BasicEvent& operator = (const BasicEvent& e); }; AbstractEvent定义为: template <class TArgs, class TStrategy, class TDelegate, class TMutex = FastMutex> class AbstractEvent { public: AbstractEvent(): _executeAsync(this, &AbstractEvent::executeAsyncImpl), _enabled(true) { } AbstractEvent(const TStrategy& strat): _executeAsync(this, &AbstractEvent::executeAsyncImpl), _strategy(strat), _enabled(true) { } virtual ~AbstractEvent() { } void operator += (const TDelegate& aDelegate) { typename TMutex::ScopedLock lock(_mutex); _strategy.add(aDelegate); } void operator -= (const TDelegate& aDelegate) { typename TMutex::ScopedLock lock(_mutex); _strategy.remove(aDelegate); } void operator () (const void* pSender, TArgs& args) { notify(pSender, args); } void operator () (TArgs& args) { notify(0, args); } void notify(const void* pSender, TArgs& args) { Poco::ScopedLockWithUnlock<TMutex> lock(_mutex); if (!_enabled) return; TStrategy strategy(_strategy); lock.unlock(); strategy.notify(pSender, args); } ActiveResult<TArgs> notifyAsync(const void* pSender, const TArgs& args) { NotifyAsyncParams params(pSender, args); { typename TMutex::ScopedLock lock(_mutex); params.ptrStrat = SharedPtr<TStrategy>(new TStrategy(_strategy)); params.enabled = _enabled; } ActiveResult<TArgs> result = _executeAsync(params); return result; } // ....... protected: struct NotifyAsyncParams { SharedPtr<TStrategy> ptrStrat; const void* pSender; TArgs args; bool enabled; NotifyAsyncParams(const void* pSend, const TArgs& a):ptrStrat(), pSender(pSend), args(a), enabled(true) { } }; ActiveMethod<TArgs, NotifyAsyncParams, AbstractEvent> _executeAsync; TArgs executeAsyncImpl(const NotifyAsyncParams& par) { if (!par.enabled) { return par.args; } NotifyAsyncParams params = par; TArgs retArgs(params.args); params.ptrStrat->notify(params.pSender, retArgs); return retArgs; } TStrategy _strategy; /// The strategy used to notify observers. bool _enabled; /// Stores if an event is enabled. Notfies on disabled events have no effect /// but it is possible to change the observers. mutable TMutex _mutex; private: AbstractEvent(const AbstractEvent& other); AbstractEvent& operator = (const AbstractEvent& other); };
从AbstractEvent类中,我们看到AbstractEvent类中存在了一个TStrategy的对象_strategy。接口上则重载了+=函数,用来把所需的目标对象加入_strategy中,完成注册功能。重载了operator (),用于触发事件。
于是同步事件所有的步骤便被串了起来。
5.2 异步事件
理解了同步事件后,让我们来看异步事件。这还是让我们从一个例子说起:#include "Poco/BasicEvent.h" #include "Poco/Delegate.h" #include "Poco/ActiveResult.h" #include <iostream> using Poco::BasicEvent; using Poco::Delegate; using Poco::ActiveResult; class TargetAsync { public: void onAsyncEvent(const void* pSender, int& arg) { std::cout << "onAsyncEvent: " << arg << " Current Thread Id is :" << GetCurrentThreadId() << " "<< std::endl; return; } }; template<typename RT> class Source { public: BasicEvent<int> theEvent; ActiveResult<RT> AsyncFireEvent(int n) { return ActiveResult<RT> (theEvent.notifyAsync(this, n)); } }; int main(int argc, char** argv) { Source<int> source; TargetAsync target; std::cout << "Main Thread Id is :" << GetCurrentThreadId() << " " << std::endl; source.theEvent += Poco::delegate(&target, &TargetAsync::onAsyncEvent); ActiveResult<int> Targs = source.AsyncFireEvent(43); Targs.wait(); std::cout << "onEventAsync: " << Targs.data() << std::endl; source.theEvent -= Poco::delegate(&target, &TargetAsync::onAsyncEvent); return 0; }例子里可以看出,同同步事件不同的是,触发事件时,我们调用的是notifyAsync接口。在这个接口里,NotifyAsyncParams对象被创建,并被交由一个主动对象_executeAsync执行。关于主动对象ActiveMethod的介绍,可以从前面的文章POCO C++库学习和分析 -- 线程 (四)中找到。
(版权所有,转载时请注明作者和出处 http://blog.csdn.net/arau_sh/article/details/8673557)