Nominate our 2022 Qt Champions!

Connect one object to many objects of same type.

  • So I have the following setup:

                   /--- Device[1]
    Controller ---+---- Device[2]
                   \--- Device[3]

    Each object is in its own thread.

    However, I am unsure how is best to do the same in the other direction (controller to device). The best I have come up with is a similar scheme, where I connect a signal in controller to a slot in each of the devices. I can still pass an index so if the messages is directed at Device[1], then Device[2] and Device[3] can ignore it. However, is this a huge overhead in terms of data repetition? - i.e. is the data sent three times?

    Is there a better way?

  • Hi indeed you can send the index, or you can use QObject::sender() method which is returning pointer to emitting QObject to identify who sent the message, or you can use QSignalMapper to add index.

  • Hi Thanks for that.

    In the case where controller needs to send something to a specific device, how can that be done?

    If I connect "send" signal in controller, to each of the slots then I may get the data sent three times... which is not efficient.

    I could connect separate signals to each device, but I have to then create the signals dynamically because I only know how many devices I will have at run time... not sure if that is possible....?

  • Well then just call the method at the object directly, QMetaObject::invokeMethod(..., Qt::QueuedConnection), to make it thread save.

  • @yeckel
    Do you mean in the controller, keep a list of device pointer that are in different threads and then do something like:

    edit - corrected the syntax
    QMetaObject::invokeMethod(devices[x], "handleMessage", Qt::QueuedConnection, Q_ARG(QByteArray msgData)));

    Is that right? - is that not breaking the whole signal/slots methodology?

  • So invoking slots directly works, but it breaks the whole methodology of slots/signals, because we are invoking a slot in a foreign object without a signal or connection... effectively punching a hole through the wall and grabbing what we want - so it also breaks the encapsulation principals too (not the mention thread boundary).

    So finally I decided I have to dream something up else...
    Because I only know how many devices I will have at run time, creating signals dynamically as each device registers is appealing and possible, but its not very easy to do and not maintainable.

    However we can instantiate objects dynamically, where the object contains a signal that can be used to map one-to-one to a device. Effectively creating a "postbox" object where its signal can be connected to the equivalent device at run-time. The setup is shown below:

    |                         |
    |             Postbox[1]--+------Device[1]
    | Controller  Postbox[2]--+------Device[2]
    |             Postbox[3]--+------Device[3]
    |                         |   :
    |             Postbox[x]--+------Device[x]
    |                         |

    Where the controller object contains an array (or vector) of postbox objects. For each device that registers with the controller it creates a new instance of postbox, connects its signal to the devices slot and then adds the postbox to the array. Then to post a message to, for example device[3], the controller simply calls a function like postboxes[3].postmessage(msgData);, the postmessage function emits the signal that is connected to the device[3].

    As far as I can tell, this is the only "correct/simple" way to do this because qt slots/signals does not seem to be setup to do message routing as such. Please someone correct me if I am wrong!

  • Lifetime Qt Champion


    Maybe QSignalMapper could also be a solution

  • @SGaist

    I looked into QSignalMapper, but it seems to have limitations, for example you can only pass one parameter and you can really just map one slot/signal.

    I could probably make it work by passing a structure (and register that with the meta compiler), and then use multiple QSignalMappers... but, at least for my case, I find that creating my own object to be much simpler both to implement and to read.