(使用“信号处理程序”一词,我指的是槽,而不是用于POSIX信号的处理程序。)
我需要将未知子类的QObject实例的所有信号(可能不直接使用
(通过“未知”我意思是我的代码应该尽可能通用。因此,它不应该包含对我在RPC系统中使用的每个类中的每个信号的
我需要将未知子类的QObject实例的所有信号(可能不直接使用
QObject :: connect
)连接到另一个QObject的单个槽上。 我需要这样做是为了通过网络发送信号及其参数(用于支持信号的自己的RPC系统)。(通过“未知”我意思是我的代码应该尽可能通用。因此,它不应该包含对我在RPC系统中使用的每个类中的每个信号的
connect
语句,而是提供类似 RPC :: connectAllSignals(QObject *); 的内容,然后在运行时扫描所有信号并连接它们。)
我想要实现的是:处理所有信号并对其进行序列化(信号名称+参数)。 我已经可以对参数进行序列化,但我不知道如何获取信号名称。 经过谷歌搜索,似乎不可能像QObject实例那样使用类似的东西 sender()。 因此,我需要做一些更复杂的事情。
我当前用于将参数传递到远程端目标函数的类型系统是受限制的。 (那是因为我需要 qt_metacall
,它希望参数为 void * 类型,后面是“正确的类型”。我的RPC系统在内部使用只有几种类型的QVariants,并使用自定义方法将它们转换为 void * 的正确类型。我听说过 QVariant :: constData 太晚了,已经无法使用,而且它可能也不适合;因此,如果没有缺点,我将坚持自己的类型转换。)
所有信号应映射到的目标槽应类似于:
void handleSignal(QByteArray signalName, QVariantList arguments);
最好的解决方案是支持C++03,所以如果不使用可变参数模板,则只需使用C++11。在这种情况下,C++11也可以,因此我也很高兴看到使用C++11的答案。
现在我正在考虑的问题的可能解决方案:
我可以使用其QMetaObject扫描对象的所有信号,然后为每个信号创建一个QSignalMapper(或类似传递所有参数的东西)。这很容易,我不需要在这部分得到帮助。如前所述,我已经受到某些类型参数的限制,并且我也可以接受对参数计数的限制。
听起来像是一个肮脏的hack,但我可以使用一些自定义的基于模板的信号映射器,例如这样(在这个例子中有三个参数):
template<class T1, class T2, class T3>
class MySignalMapper : public QObject
{
Q_OBJECT
public:
void setSignalName(QByteArray signalName)
{
this->signalName = signalName;
}
signals:
void mapped(QByteArray signalName, QVariantList arguments);
public slots:
void map(T1 arg1, T2 arg2, T3 arg3)
{
QVariantList args;
// QVariant myTypeConverter<T>(T) already implemented:
args << myTypeConverter(arg1);
args << myTypeConverter(arg2);
args << myTypeConverter(arg3);
emit mapped(signalName, args);
}
private:
QByteArray signalName;
};
那么我可以像这样连接一个名为method
(已知为信号)的QMetaMethod到一个名为obj
的QObject上(可能是使用某种脚本生成所有支持的类型和参数计数的...是的...变得有点复杂了!):
// ...
}
else if(type1 == "int" && type2 == "char" && type3 == "bool")
{
MySignalMapper<int,char,bool> *sm = new MySignalMapper<int,char,bool>(this);
QByteArray signalName = method.signature();
signalName = signalName.left(signalName.indexOf('(')); // remove parameters
sm->setMember(signalName);
// prepend "2", like Qt's SIGNAL() macro does:
QByteArray signalName = QByteArray("2") + method.signature();
// connect the mapper:
connect(obj, signalName.constData(),
sm, SLOT(map(int,char,bool)));
connect(sm, SIGNAL(mapped(int,char,bool)),
this, SLOT(handleSignal(const char*,QVariantList)));
}
else if(type1 == ...)
{
// ...
虽然这种方法可能行得通,但它确实是一个不太优雅的解决方案。我需要大量的宏来覆盖最多N
个参数的所有类型组合(其中N
约为3到5,尚未确定),或者一个简单的脚本为所有情况生成代码。问题在于,这将涉及很多情况,因为我支持每个参数大约70种不同的类型(10种基本类型+嵌套列表和深度为2的映射,针对每种类型都有)。因此,对于参数计数限制为N
,需要覆盖N
^ 70个情况!
是否有完全不同的方法可以实现这个目标,而我却忽视了?
更新:
我自己解决了这个问题(请参见答案)。如果您对完整的源代码感兴趣,请查看我刚刚发布的RPC系统的bitbucket存储库:bitbucket.org/leemes/qtsimplerpc
Q_OBJECT
类成为模板。 - Lol4t0QSignalSpy
和http://qt-apps.org/content/show.php/Conan+-+Connection+analyzer+for+Qt?content=108330这样的东西。 - HostileFork says dont trust SE