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的类图:![](https://img-my.csdn.net/uploads/201303/15/1363317829_6733.jpg)
下面另一张图是Poco中Event流动的过程:
![](https://img-my.csdn.net/uploads/201303/15/1363321278_7686.jpg)
从图上看实现事件的类被分成了几类:
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>> _delegate
DefaultStrategy可以被理解成一组目标的代理。在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)