【发布时间】:2012-06-04 00:46:35
【问题描述】:
(“信号处理程序”是指插槽,而不是 POSIX 信号的处理程序。)
我需要“连接”(可能不是直接使用QObject::connect)所有信号从 QObject 的(未知)子类的实例到 另一个 QObject 的单个插槽。我需要这个来通过网络发送信号(带参数)(对于支持信号的自己的 RPC 系统)。
(对于“未知”,我的意思是我的代码应该尽可能通用。所以它不包含connect 声明,用于我在 RPC 系统中使用的每个类中的每个信号,但提供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 得到答案。
现在我对我正在考虑的问题的可能的解决方案:
我可以使用它的QMetaObject 扫描对象的所有信号,然后为 each 信号创建一个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;
};
然后我可以像这样连接一个名为obj 的QObject 的一个名为method(已知是一个信号)的QMetaMethod(可能使用某种脚本为所有支持的类型和参数计数生成.. . 是的... 它变脏了!):
// ...
}
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_OBJECTclasstemplate'd -
你可能会为这种能力寻找的关键字是“间谍”(如在 Spy++ 中)......先例(尽管不一定是你需要的)......像
QSignalSpy和qt-apps.org/content/show.php/… -
@Lol4t0 天哪,我完全忘记了(没有测试)。但是如果我需要对每种类型的组合进行硬编码,我可以使用信号处理程序来完成。脏脏脏脏的……
-
@HostileFork 感谢您提供此链接,这使我找到了一个可能的解决方案,如我自己对这个问题的回答中所述。让我们看看它是否有效。
-
@HostileFork 根据您的评论查看我的更新答案以获取解决方案。我希望我能给你这方面的声誉......
标签: c++ qt metaprogramming rpc