本文是 《基于 Qt 实现消息总线》的其中一节,建议全章阅读。
针对消息,主要有订阅、取消订阅、发布 3 种操作。基于 Qt 的信号-槽来实现,是很简单的。
class QTEVENTBUS_EXPORT QMessageBase : public QObject
{
Q_OBJECT
signals:
void on_message(QMessageResultPointer data);
};
对应每种消息,都有一个消息维护对象,其基类(QMessageBase)上,有一个信号(on_message)。发布消息的时候,会触发该信号。信号参数是一个 QMessageResultPointer 的对象,保存了共享的消息内容和维护反馈结果的相关数据。
具体的消息维护对象是泛型类,与消息类型关联,在一个总线中,每种消息维护对象只有一个实例,从而维护了发布与订阅的关联性:
template<typename T>
class QMessage : public QMessageBase
{
};
订阅的时候,连接一下信号就可以了:
template<typename T>
template<typename F>
bool QMessage<T>::subscribe(QObject const * c, F f)
{
QObject::connect(this, &QMessageBase::on_message, c,
[f](QMessageResultPointer const & e){e.invoke<T, R>(f);});
......
return true;
}
我们正是利用 Qt 信号连接来记录对该类型消息的订阅,从而简化的实现。取消订阅,实际上就是断开信号连接:
template <typename T>
bool QMessage<T>::unsubscribe(QObject const * c)
{
QObject::disconnect(this, &QMessageBase::on_message, c, 0);
......
return true;
}
这里我们要求提供上下文 QObject 对象,一方面是为了可以准确地取消与某个上下文相关的订阅;另一方面,也提供了订阅者处理消息的线程上下文,实现跨线程消息传递。
当发布消息时,只需要触发信号:
template <typename T>
void QMessage<T>::publish(T const & msg)
{
int n = receivers(SIGNAL(on_message(QMessageResultPointer)));
if (n == 0)
return;
QMessageResultPointer data(msg);
emit on_message(std::move(data));
}
通过 receivers() 方法,可以知道当前有多少个信号连接(也就是有多少个订阅者),如果没有连接,直接返回,可以提高一下处理效率。