Is it possible to have pure virtual slots?
-
I have a based class which creates several slots:
virtual void connected() = 0; virtual void dataIn() = 0; virtual void disconnected() = 0;In my derived class I implement these slots:
void connected() override; void dataIn() override; void disconnected() override;I'm asking if this is possible because since I've done this I'm getting some very odd behaviour which I cannot explain, execution is halting in internal Qt functions.
[Edit] I just read here:
https://stackoverflow.com/questions/2998216/does-qt-support-virtual-pure-slotsThat the answer is yes, but the override keyword is not required and the driver functions should not appear under a public slots section...but having done that I'm still getting:
'function name' overrides a member function but is not marked 'override'For each overridden slot, this does not explain the strange behaviour.
-
Here is the demo I just created and it works, so the problem has to be specific to the class:
class clsBaseTest : public QObject { Q_OBJECT public slots: //Pure virtual slot virtual void onTest() = 0; public: explicit clsBaseTest() {} signals: void test(); }; class clsDerivedTest : public clsBaseTest { public: clsDerivedTest() {} virtual void onTest() override { qDebug() << "clsDerivedTest::onTest"; }; };Code to test:
clsDerivedTest test; connect(&test, SIGNAL(test()), &test, SLOT(onTest())); emit test.test();In the Application Output I see exactly what I am supposed to see:
2020-11-06 18:47:48.956472+0000 mdFileIO[3144:63052] clsDerivedTest::onTestSo it must be the fact that the connections are established in the base class constructor, I will try moving these to another function.
@SPlatten , I moved the socket connection from the constructor to its own function:
void clsModHelper::connectToXMLMPAM() { //Connect to the Application connectToHost(clsModHelper::mscpszHost, muint16Port); if ( waitForConnected(clsModHelper::mscintConnectionTimeout) != true ) { throw -1; } }main loops like this:
int main(int intArgc, char* parystrArgv[]) { //Initialise the module QApplication a(intArgc, parystrArgv); clsModFileIO obj(&a, intArgc, parystrArgv); try{ obj.connectToXMLMPAM(); } catch( exception& e) { std::cout << "Could not connect to XMLMPAM!"; exit(EXIT_FAILURE); } return obj.intExitCode(); }Now it works, thank you to everyone who helped me to resolve this issue.
-
Hi,
What strange behavior do you get ?
-
@SGaist I had a break point on:
if ( waitForConnected(clsModHelper::mscintConnectionTimeout) ) {I've set the mscintConnectionTimeout to 20000 ms. When I click on the step over icon, the debugger jumps into qobjectdefs_impl.h and stops on line 152:
(o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); -
What if you have a pure virtual with a default implementation ?
-
@SGaist , updated....if I comment out the 3 connects which:
// connect(this, &QTcpSocket::connected, this, &clsModHelper::connected); // connect(this, &QTcpSocket::disconnected, this, &clsModHelper::disconnected); // connect(this, &QIODevice::readyRead, this, &clsModHelper::dataIn);It doesn't crash and the connection is made, waitForConnected doesn't crash and the application continues to where I expect.
To clarify, in the base class, the initial prototype for these slots:
public slots: virtual void connected() = 0; virtual void dataIn() = 0; virtual void disconnected() = 0;Then in my derived class:
public slots: void connected() override; void dataIn() override; void disconnected() override;The implementation, just to prove if works:
void clsModFileIO::connected() { //Start the module thread start(); } void clsModFileIO::dataIn() { qdbg() << "dataIn"; } void clsModFileIO::disconnected() { emit terminateModule(); }If I then uncomment those connections and rebuild it does exactly the same its very repeatable.
-
I used to have pure virtual slots. It's quite a long time ago so I was using the old syntax to connect. Maybe you can try if that will work.
P.S. I think the "override" mark is irrelevant, you can add that. The SO post is so old that c++11 was not published at that time. -
I used to have pure virtual slots. It's quite a long time ago so I was using the old syntax to connect. Maybe you can try if that will work.
P.S. I think the "override" mark is irrelevant, you can add that. The SO post is so old that c++11 was not published at that time. -
@Bonnie , thank you, the only reason I put in the override keyword is to get rid of the annoying yellow text appended at the end of each line suggesting it.
-
I used to have pure virtual slots. It's quite a long time ago so I was using the old syntax to connect. Maybe you can try if that will work.
P.S. I think the "override" mark is irrelevant, you can add that. The SO post is so old that c++11 was not published at that time.@Bonnie , just replaced connections with:
connect(this, SIGNAL(connected), this, SLOT(connected)); connect(this, SIGNAL(disconnected), this, SLOT(disconnected)); connect(this, SIGNAL(readyRead), this, SLOT(dataIn));It worked, no more crashing, thank you.
-
@SPlatten ok, than this
connect(this, &QTcpSocket::connected, this, &clsModHelper::connected);is explicitly invoking the pure virtual base function, thats bound to crash
connect(this, &QTcpSocket::connected, this, & clsModFileIO::connected);should work fine
-
@SPlatten ok, than this
connect(this, &QTcpSocket::connected, this, &clsModHelper::connected);is explicitly invoking the pure virtual base function, thats bound to crash
connect(this, &QTcpSocket::connected, this, & clsModFileIO::connected);should work fine
@J-Hilk , thank you, but isn't this the point of using a pure virtual slot, I want the actual derived slot to be worked out by the compiler so I can have any number of derived classes based the same pure virtual slot and then the actual slot gets called in its place?
-
I used to have pure virtual slots. It's quite a long time ago so I was using the old syntax to connect. Maybe you can try if that will work.
P.S. I think the "override" mark is irrelevant, you can add that. The SO post is so old that c++11 was not published at that time.@Bonnie , not so fast, I thought it worked because it didn't crash, but now I see in the Application Output:
2020-11-06 09:11:49.460550+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::connected in ../mdFileIO/clsModHelper.cpp:67 2020-11-06 09:11:49.460576+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::disconnected in ../mdFileIO/clsModHelper.cpp:68 2020-11-06 09:11:49.460583+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::readyRead in ../mdFileIO/clsModHelper.cpp:69 -
@Bonnie , not so fast, I thought it worked because it didn't crash, but now I see in the Application Output:
2020-11-06 09:11:49.460550+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::connected in ../mdFileIO/clsModHelper.cpp:67 2020-11-06 09:11:49.460576+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::disconnected in ../mdFileIO/clsModHelper.cpp:68 2020-11-06 09:11:49.460583+0000 mdFileIO[4406:86031] QObject::connect: Parentheses expected, signal clsModHelper::readyRead in ../mdFileIO/clsModHelper.cpp:69@SPlatten
The old syntax is not like that :)
As the output says you need parentheses likeconnect(this, SIGNAL(connected()), this, SLOT(connected())); connect(this, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(this, SIGNAL(readyRead()), this, SLOT(dataIn())); -
@SPlatten
The old syntax is not like that :)
As the output says you need parentheses likeconnect(this, SIGNAL(connected()), this, SLOT(connected())); connect(this, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(this, SIGNAL(readyRead()), this, SLOT(dataIn())); -
@J-Hilk , thank you, but isn't this the point of using a pure virtual slot, I want the actual derived slot to be worked out by the compiler so I can have any number of derived classes based the same pure virtual slot and then the actual slot gets called in its place?
@SPlatten sure, but you can always call the base implementation by implicitly invoking it and in many cases you want to do that.
take for example:
class BaseClass { public: virtual void functionA() { qDebug () << "A"; } }; class DerivedClass : public BaseClass{ public: void functionA() override {qDebug() << "B";} }; class DerivedClass2: public BaseClass { public: void functionA() override { BaseClass::functionA(); qDebug() << "B"; } }; int main (int argc, char *argv[]) { DerivedClass c1; DerivedClass2 c2; c1.functionA(); qDebug() << "----"; c2.functionA(); }this outputs:
B ---- A Bwhere the 2nd class implicitly invokes the base implementation.
-
@Bonnie, its different, but still not right, now it stops in moc_clsModHelper.cpp on a line which reads:
case 4: _t->connected(); break;The Application Output shows:
libc++abi.dylib: Pure virtual function called! -
@SPlatten sure, but you can always call the base implementation by implicitly invoking it and in many cases you want to do that.
take for example:
class BaseClass { public: virtual void functionA() { qDebug () << "A"; } }; class DerivedClass : public BaseClass{ public: void functionA() override {qDebug() << "B";} }; class DerivedClass2: public BaseClass { public: void functionA() override { BaseClass::functionA(); qDebug() << "B"; } }; int main (int argc, char *argv[]) { DerivedClass c1; DerivedClass2 c2; c1.functionA(); qDebug() << "----"; c2.functionA(); }this outputs:
B ---- A Bwhere the 2nd class implicitly invokes the base implementation.