将一个对象连接到许多相同类型的对象
Connect one object to many objects of same type
所以我有以下设置:
|---- Device[1]
Controller ---+---- Device[2]
|---- Device[3]
| :
|---- Device[x]
每个对象都在自己的线程中。
对于从device [x]到控制器的通信,这相对容易,我只是将所有设备信号连接到控制器中的插槽,并将设备索引作为参数传递,以便控制器可以识别哪个设备。
然而,我不确定如何最好地在另一个方向(控制器到设备)做同样的事情。我想到的最好的方案是一个类似的方案,我将控制器中的信号连接到每个设备的插槽。我仍然可以传递一个索引,这样如果消息指向Device[1],那么Device[2]和Device[3]可以忽略它。然而,就数据重复而言,这是一个巨大的开销吗?-即数据是否发送三次?
有更好的方法吗?
编辑1 我已经编辑了这个例子,以显示我可以有任何数量的设备。它不是硬编码的数字,所以如果我需要每个设备的信号/插槽,信号将需要动态创建。
编辑2 似乎你可以直接调用方法,像这样:QMetaObject::invokeMethod(devices[x], "handleMessage", Qt::QueuedConnection, Q_ARG(QByteArray msgData)));
这允许我调用任何对象上的槽(排队,所以它是线程安全的)并传递我的数据....但如果我这样做,感觉就像我打破了基本的Qt槽/信号方法。
所以我在网上没有找到很多关于这个问题的信息。我看到人们尝试用以下几种方式来回答这个问题:
1。QSignalMapper
这有它的局限性,我已经在ram的回答中评论过了。
2。直接调用目标槽
您可以直接调用目标槽(可以跨线程边界!),这可以如下所示完成(只是为了完整性而显示):
QMetaObject::invokeMethod(devices[x], "handleMessage", Qt::QueuedConnection, Q_ARG(QByteArray msgData)));
地点:
- Devices[x]是指向设备对象 的指针。
- "handleMessage"是槽函数的名称
- 排队连接用于确保其线程安全,并且不会在调用线程中被调用。
- Q_ARG(…)是传递给插槽的参数,在本例中是名为msgData的QByteArray。注意:您可以设置多个。
这工作得很好,但它打破了槽/信号的整个方法,因为我们在没有信号或连接的情况下调用外部对象中的槽…有效地在墙上打一个洞,抓住我们想要的东西——所以它也打破了封装原则(更不用说线程边界了)。
3。动态槽位创建
我在qt文档中读到这个,大约一半,但它看起来像可怕的代码,必须编写/维护,所以我甚至没有尝试这个。
我建议的解决方案
所以最后我决定我必须梦想一些东西。因为我只知道在运行时将有多少个设备,所以在每个设备寄存器时动态创建信号很吸引人,但如上所述,不是很好。
但是,我们可以动态实例化对象,其中对象包含一个可用于一对一映射到设备的信号。有效地创建一个"邮筒"对象,其信号可以在运行时连接到等效设备。设置如下所示:
|-------------------------|
| |
| Postbox[1]--+------Device[1]
| Controller Postbox[2]--+------Device[2]
| Postbox[3]--+------Device[3]
| | :
| Postbox[x]--+------Device[x]
| |
|-------------------------|
其中控制器对象包含一个邮箱对象数组(或向量)。对于每个向控制器注册的设备,它创建一个新的postbox实例,将其信号连接到设备槽,然后将邮箱添加到数组中。然后要发送消息到,例如设备[3],控制器只需调用postboxes[3].postmessage(msgData);
这样的函数,postmessage函数发出连接到设备[3]的信号。
据我所知,这是唯一"正确/简单"的方法来做到这一点,因为qt槽/信号似乎没有被设置为这样做消息路由。如果我说错了,请有人纠正我!
使用QSignalMapper
Controller::Controller()
{
QSignalMapper* signalMapper = new QSignalMapper(this);
for (int i = 0; i < deviceCount; ++i) {
Device* d = new Device(...);
connect(d, SIGNAL(somethingHappened)), signalMapper, SLOT(map()));
signalMapper->setMapping(d, i);
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handleDevice(int)));
}
void Controller::handleDevice(int id)
{
.....
}
更多信息:http://doc.qt.io/qt-5/qsignalmapper.html
- 我收到同义重复编译器错误。我应该如何修复"类型"X"的参数与类型"X"的参数不兼容?
- 为什么许多标准库类型在 C++20 中删除 operator!=?
- 为什么我不能在同一行中定义两个相同类型的类的成员指针
- Doxygen,当参数类型定义使签名相同时,如何拆分函数文档?
- CRTP 模式 但是在数据结构中存储非同构类型
- C 呼叫功能具有许多不同类型
- 函数类型定义可以与外部"C"相关联吗?
- 如何处理许多不同的输入二进制消息类型
- 当两种类型相同时,专用于 C++ 模板函数
- 重构许多函数以优雅地接收任何类型的参数(模板<any>)
- 我如何实现递归函数的模板,该模板允许C 中的许多不确定数据类型的参数
- 通过许多不同类型的队列进行迭代器
- 使用许多类型运算符过载时,模棱两可的超载
- 当可能的许多派生类型时,从基本类型的实例中获取派生类型
- 在C API中定义了一个类型,如何将其与命名空间中的C++类相关联
- 类和变量声明(相同类类型)
- 私有结构(在类中定义)不能用作属于同一类的函数的返回类型吗
- 用于将静态字符串常量与类型相关联的类型特征模式
- 访问本地对象(相同类类型)的私有成员
- 指向相同类类型设计可行性的指针向量