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>>  _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)

posted @ 2013-03-15 12:23  在天与地之间  阅读(2886)  评论(0编辑  收藏  举报