Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Multiple Inheritance of Interfaces with Slots



  • Hello,

    I need some advise on how I can achieve to implement multiple interfaces with Qt.
    I got a class which handles communication to different periphiere-cards on an RS485-bus 'PeripherieCommunicator'.
    This class includes several interfaces, each declaring the communication to one of these cards in the RS485-bus, for example:

    class IPR112Control
    {
    public:
        virtual ~IPR112Control() {}
    public slots:
        virtual signed short ssPR112ReadShortState( STATE * pstrShortState, int addr) = 0;
    };
    Q_DECLARE_INTERFACE(IPR112Control, "IPR112Control/1.0")
    

    and

    class IPR144Control
    {
    public:
        virtual ~IPR144Control() {}
    public slots:
        virtual signed short ssPR144GetFullDeviceInfo( INFO * pstrFullDeviceInfo, int addr ) = 0;
    };
    Q_DECLARE_INTERFACE(IPR144Control, "IPR144Control/1.0")
    

    My class PeripherieCommunicator implements those 2 interfaces:

    class PeripherieCommunicator : public QObject, IPR112Control,  public IPR144Control
    {
        Q_OBJECT
        Q_INTERFACES(IPR112Control)
        Q_INTERFACES(IPR144Control)
    ...
    // IPR112Control interface
    public slots:
        signed short ssPR112ReadShortState( STATE * pstrShortState, int addr);
    // IPR144Control interface
    public slots:
        signed short ssPR144GetFullDeviceInfo( INFO * pstrFullDeviceInfo, int addr ) = 0;
    }
    

    The Problem occurs when I try to connect to those slots using the Interface, i.e.:

    MotorControl::MotorControl(IPR112Control *pcom, QObject *parent) : QObject(parent)
    {
        this->pcom = pcom;
        connect(this, &MotorControl::readShortState, this->pcom, &IPR112Control::ssPR112ReadShortState, Qt::ConnectionType::BlockingQueuedConnection);
    

    The problem seems to be that my Interface IPR112Control (and other interfaces) does not inherit from QObject but when I change them to inherit QObject I get errors in my PeripherieCommunicator class since the reference to QObject is ambigous then.
    Is there any advice on how to fix this problem in a proper way? Of course I could just inject the pointer to 'PeripherieCommunicator' in the constructor of 'MotorControl' but this would make the class less testable. The other way I see is to merge all Interfaces into one single interface but this does not seem the right way either...



  • Try changing it to connect(this, &MotorControl::readShortState, this, std::bind(&IPR112Control::ssPR112ReadShortState, this->pcom, std::placeholders::_1, std::placeholders::_2), Qt::ConnectionType::BlockingQueuedConnection);

    The only problem now is that you need to make sure this->pcom does not get destroyed while there's still a pending slot to be executed and this was destroyed.

    If that's a problem then save the return of the connect: pcomConnection = QObject::connect(this, &MotorControl::readShortState, this->pcom, std::bind(&IPR112Control::ssPR112ReadShortState,std::placeholders::_1,std::placeholders::_2), Qt::ConnectionType::BlockingQueuedConnection); and then call QObject::disconnect(pcomConnection); before the deletion of this->pcom and replace the deletion with a delayed one:

    auto pcomToDelete = this->pcom;
    QTimer::singleShot(0,[pcomToDelete]()->void{delete pcomToDelete;});
    

  • Qt Champions 2019

    Wouldn't a simple cast to IPR112Control help here?

    IPR112Control *ifc = pcom;
    connect(this, &MotorControl::readShortState, ifc, &IPR112Control::ssPR112ReadShortState, Qt::ConnectionType::BlockingQueuedConnection);
    


  • @Christian-Ehrlicher no, since pcom gets already injected as IPR112Control. That connect would also fail since IPR112Control does not inherit from QObject.



  • @Christian-Ehrlicher the problem is the 3rd argument (receiver/context), not the slot, I think



  • Yes, but i already hat pcom of type IPR112Control but you were right with the 3rd argument.
    I did solve the problem now with a dynamic cast and using the old connection-syntax

    connect(this, SIGNAL(readShortState(STATE, int)), dynamic_cast<QObject*>(this->pcom), SLOT(ssPR112ReadShortState(STATE, int)));
    

    This way I lose the compile time checking but this seems to be the smallest tradeoff.



  • @kain said in Multiple Inheritance of Interfaces with Slots:

    This way I lose the compile time checking but this seems to be the smallest tradeoff.

    My suggestion above didn't work? I'm surprised dynamic_cast works, it should only accept reinterpret_cast there...


  • Qt Champions 2017

    @Christian-Ehrlicher said in Multiple Inheritance of Interfaces with Slots:

    Wouldn't a simple cast to IPR112Control help here?

    Nope. This will fail the static assertions in QObject::connect.

    @VRonin said in Multiple Inheritance of Interfaces with Slots:

    Try changing it to connect(this, &MotorControl::readShortState, this, std::bind(&IPR112Control::ssPR112ReadShortState, this->pcom, std::placeholders::_1, std::placeholders::_2), Qt::ConnectionType::BlockingQueuedConnection);

    Correct, however second point is moot. In fact you should set the proper context (this is incorrect here):

    QObject * context = qobject_cast<QObject *>(pcom);
    QObject::connect(this, &MotorControl::readShortState, context, std::bind(&IPR112Control::ssPR112ReadShortState, pcom, std::placeholders::_1, std::placeholders::_2), Qt::ConnectionType::BlockingQueuedConnection);
    

    Now the connection is broken cleanly whenever context (which is pcom) gets destroyed. No additional work's needed.

    I'm surprised dynamic_cast works, it should only accept reinterpret_cast there...

    Why? That's the whole reason dynamic_cast exists in the first place - to runtime cast polymorphic types.


  • Qt Champions 2019

    @kshegunov said in Multiple Inheritance of Interfaces with Slots:

    Nope. This will fail the static assertions in QObject::connect.

    Then just use a lambda :)


Log in to reply