Solved 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 andthis
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 callQObject::disconnect(pcomConnection);
before the deletion ofthis->pcom
and replace the deletion with a delayed one:auto pcomToDelete = this->pcom; QTimer::singleShot(0,[pcomToDelete]()->void{delete pcomToDelete;});
-
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-syntaxconnect(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 acceptreinterpret_cast
there... -
@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 ispcom
) 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. -
@kshegunov said in Multiple Inheritance of Interfaces with Slots:
Nope. This will fail the static assertions in QObject::connect.
Then just use a lambda :)