Is it possible to have pure virtual slots?
-
@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.
-
@Bonnie , sure:
These are very much a work in progress:
clsModHelper.h
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); public slots: virtual void connected() = 0; virtual void dataIn() = 0; virtual void disconnected() = 0; void errorOccurred(QAbstractSocket::SocketError socketError); void exitModule(); void killThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };clsModFileIO.h:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } public slots: void connected() override; void dataIn() override; void disconnected() override; void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };Some additional information:
Qt Creator 4.13.2 Based on Qt 5.15.1 (Clang 11.0 (Apple), 64 bit) Built on Oct 1 2020 01:16:45 From revision 2ee1af2032Using Qt 5.15.0
-
@Bonnie , sure:
These are very much a work in progress:
clsModHelper.h
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); public slots: virtual void connected() = 0; virtual void dataIn() = 0; virtual void disconnected() = 0; void errorOccurred(QAbstractSocket::SocketError socketError); void exitModule(); void killThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };clsModFileIO.h:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } public slots: void connected() override; void dataIn() override; void disconnected() override; void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };Some additional information:
Qt Creator 4.13.2 Based on Qt 5.15.1 (Clang 11.0 (Apple), 64 bit) Built on Oct 1 2020 01:16:45 From revision 2ee1af2032Using Qt 5.15.0
@SPlatten
As I remember, the overridden "slots" in the derived class can't be declared as slots.
So trypublic: void connected() override; void dataIn() override; void disconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd);BTW, you don't have Q_OBJECT in clsModFileIO class but you can declare slots in it? Is that possible?
-
@SPlatten
As I remember, the overridden "slots" in the derived class can't be declared as slots.
So trypublic: void connected() override; void dataIn() override; void disconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd);BTW, you don't have Q_OBJECT in clsModFileIO class but you can declare slots in it? Is that possible?
-
@J-Hilk Its a pure virtual slot for the purpose of defining the structure and interface required to be implemented by the derived classes, thats why they have no implementation.
-
@Bonnie , I've changed the header to your recommendations, but I'm still ending up with:
QAbstractSocket::UnconnectedState libc++abi.dylib: Pure virtual function called!@SPlatten said in Is it possible to have pure virtual slots?:
QAbstractSocket::UnconnectedState
libc++abi.dylib: Pure virtual function called!Have you take a look at stackoverflow?
For my understanding: yes it is possible
- on base class don't forget to add
Q_OBJECTmacro - on subclass, do not add
Q_SLOTSorslotsfor those slots - and finally, don't forget to re-run
mocif you change one of those things
- on base class don't forget to add
-
As I tested, it doesn't matter where you do the connection, it is also fine to use the new syntax to connect the base class's slot.
But it is important that the pure virtual slots cannot be called in the base class's constructor (that's not Qt but c++ basic).
Do you call waitForConnected in clsModHelper's constructor?
If you do, the solution is to either move waitForConnected out of the constructor (which is the better way), or connect using Qt::QueuedConnection. -
You're doing something wrong, it works perfectly (and I don't see a reason why it should not - it's plain c++)
main.h
#pragma once #include <QObject> struct Ifc { virtual void myVirtualSlot() = 0; }; class TestClass : public QObject, public Ifc { Q_OBJECT public: TestClass(); void myVirtualSlot() override; };main.cpp
#include <QtCore> #include "main.h" TestClass::TestClass() { QTimer::singleShot(100, this, &TestClass::myVirtualSlot); } void TestClass::myVirtualSlot() { qDebug() << "Executed!"; QCoreApplication::exit(0); } int main(int argc, char** argv) { QCoreApplication app(argc, argv); TestClass c; return app.exec(); } -
@Christian-Ehrlicher , what is your issue? You seem to be constantly on the attack. There is nothing plain old C++ about Qt.
I've been clear what I have done and what the results are. If it isn't working for me then it could be down to something else.
My development system:
macOS Catalina Version 10.15.7 iMac (Retina 5K, 27-inch, Late 2015) Processor 4 GHz Quad-Core Intel Core i7 Memory 16 GB 1867 MHz DDR3 Graphics AMD Radeon R9 M395X 4 GBQt:
Qt Creator 4.13.2 Based on Qt 5.15.1 (Clang 11.0 (Apple), 64 bit) Built on Oct 1 2020 01:16:46 From revision 2ee1af2032Qt Kits:
Desktop Qt 5.14.2 clang 64bit Desktop Qt 5.15.0 clang 64bit Qt 5.14.2 WebAssembly Qt 5.14.2 for iOS Qt 5.14.2 for iOS Simulator Qt 5.15.0 WebAssembly Qt 5.15.0 for iOS Qt 5.15.0 for iOS SimulatorI am using Desktop Qt 5.15.0 clang 64bit
Compiler
Clang (C++, x86 64bit in /usr/bin) Clang (C++, x86 32bit in /ust/bin) Apple Clang (armv7) Apple Clang (x86_64) Apple Clang (i386) Apple Clang (armv7k) Apple Clang (arm64) -
@SPlatten said in Is it possible to have pure virtual slots?:
QAbstractSocket::UnconnectedState
libc++abi.dylib: Pure virtual function called!Have you take a look at stackoverflow?
For my understanding: yes it is possible
- on base class don't forget to add
Q_OBJECTmacro - on subclass, do not add
Q_SLOTSorslotsfor those slots - and finally, don't forget to re-run
mocif you change one of those things
@KroMignon The prototypes for both the base and derived classes were posted so you should see that the base includes Q_OBJECT macro.
- on base class don't forget to add
-
@Christian-Ehrlicher , what is your issue? You seem to be constantly on the attack. There is nothing plain old C++ about Qt.
I've been clear what I have done and what the results are. If it isn't working for me then it could be down to something else.
My development system:
macOS Catalina Version 10.15.7 iMac (Retina 5K, 27-inch, Late 2015) Processor 4 GHz Quad-Core Intel Core i7 Memory 16 GB 1867 MHz DDR3 Graphics AMD Radeon R9 M395X 4 GBQt:
Qt Creator 4.13.2 Based on Qt 5.15.1 (Clang 11.0 (Apple), 64 bit) Built on Oct 1 2020 01:16:46 From revision 2ee1af2032Qt Kits:
Desktop Qt 5.14.2 clang 64bit Desktop Qt 5.15.0 clang 64bit Qt 5.14.2 WebAssembly Qt 5.14.2 for iOS Qt 5.14.2 for iOS Simulator Qt 5.15.0 WebAssembly Qt 5.15.0 for iOS Qt 5.15.0 for iOS SimulatorI am using Desktop Qt 5.15.0 clang 64bit
Compiler
Clang (C++, x86 64bit in /usr/bin) Clang (C++, x86 32bit in /ust/bin) Apple Clang (armv7) Apple Clang (x86_64) Apple Clang (i386) Apple Clang (armv7k) Apple Clang (arm64)@SPlatten said in Is it possible to have pure virtual slots?:
If it isn't working for me then it could be down to something else.
No, it must be in your code. So do what we ask every time - provide a testcase which reproduce your problem.
And virtual functions and how they work is for sure c++ and not Qt./edit: and you should simply try out my example.
-
@SPlatten said in Is it possible to have pure virtual slots?:
If it isn't working for me then it could be down to something else.
No, it must be in your code. So do what we ask every time - provide a testcase which reproduce your problem.
And virtual functions and how they work is for sure c++ and not Qt./edit: and you should simply try out my example.
@Christian-Ehrlicher , for the 2nd time today, hear is my code:
Base class prototype, just edited to move virtual slots out of public slot definitions:
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); virtual void onConnected() = 0; virtual void onDataIn() = 0; virtual void onDisconnected() = 0; public slots: void onErrorOccurred(QAbstractSocket::SocketError socketError); void onExitModule(); void onKillThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };Derived class prototype:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } virtual void onConnected() override; virtual void onDataIn() override; virtual void onDisconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };With the code in this configuration the Application Output shows:
2020-11-06 18:04:44.443090+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onConnected() in ../mdFileIO/clsModHelper.cpp:67 2020-11-06 18:04:44.443120+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onDisconnected() in ../mdFileIO/clsModHelper.cpp:68 2020-11-06 18:04:44.443130+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onDataIn() in ../mdFileIO/clsModHelper.cpp:69As for a test case, I launch my main application which launches the problem application as a child process, immediately when this application is launched it attempts to connect to the launching application with a socket which is successful but the slots are not connected to the signals in QTcpSocket despite the connect statements being correct as far as I know.
-
I can't compile it so no - that does not help. As you can see my example works as expected so it must be somewhere in your code. If you want help you have to provide something which we can reproduce which we can't.
-
@Christian-Ehrlicher , for the 2nd time today, hear is my code:
Base class prototype, just edited to move virtual slots out of public slot definitions:
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); virtual void onConnected() = 0; virtual void onDataIn() = 0; virtual void onDisconnected() = 0; public slots: void onErrorOccurred(QAbstractSocket::SocketError socketError); void onExitModule(); void onKillThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };Derived class prototype:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } virtual void onConnected() override; virtual void onDataIn() override; virtual void onDisconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };With the code in this configuration the Application Output shows:
2020-11-06 18:04:44.443090+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onConnected() in ../mdFileIO/clsModHelper.cpp:67 2020-11-06 18:04:44.443120+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onDisconnected() in ../mdFileIO/clsModHelper.cpp:68 2020-11-06 18:04:44.443130+0000 mdFileIO[1591:37076] QObject::connect: No such slot clsModHelper::onDataIn() in ../mdFileIO/clsModHelper.cpp:69As for a test case, I launch my main application which launches the problem application as a child process, immediately when this application is launched it attempts to connect to the launching application with a socket which is successful but the slots are not connected to the signals in QTcpSocket despite the connect statements being correct as far as I know.
@SPlatten
You have some misunderstanding.
According to the SO post, the virtual functions need to be declared as slots in the base class, but not in the derived class.
So they still need to be in the "public slots" part of clsModHelper, but only "public" part of clsModFileIO.And as I posted, the original way you use to connect (the new syntax) is fine to use.
The point is, the signals cannot be emited in the base class's constuctor.
As my testing, this is the most likely situation to cause that "Pure virtual function called!" error.
So if you putwaitForConnectedinclsModHelper's constructor, theconnectedsignal will be emited in it and cause that error. -
The only way I can get rid of those messages is to move the prototypes back under the public slots area and then I get the original message:
QAbstractSocket::UnconnectedState libc++abi.dylib: Pure virtual function called!I will try to produce a simple example and see if I can replicate the issue.
-
@SPlatten
You have some misunderstanding.
According to the SO post, the virtual functions need to be declared as slots in the base class, but not in the derived class.
So they still need to be in the "public slots" part of clsModHelper, but only "public" part of clsModFileIO.And as I posted, the original way you use to connect (the new syntax) is fine to use.
The point is, the signals cannot be emited in the base class's constuctor.
As my testing, this is the most likely situation to cause that "Pure virtual function called!" error.
So if you putwaitForConnectedinclsModHelper's constructor, theconnectedsignal will be emited in it and cause that error.@Bonnie I'll give that a try. To clarify here is the implementation:
clsModHelper::clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion) : QTcpSocket(pParent) , mblnExit(false) , mdblVersion(dblVersion) , mfpDbgLog(nullptr) , mint64AppPID(QCoreApplication::applicationPid()) , mpHeartbeat(nullptr), mpThread(nullptr) , muint16Port(0), muint16LauncherPID(0), muint16SleepTime(0) { if ( intArgc < CLA_LAUNCHER_PID ) { std::cerr << "Insufficient arguments, aborting!"; exit(EXIT_FAILURE); } if ( mspThis == nullptr ) { mspThis = this; } muint16Port = (quint16)atoi(parystrArgv[CLA_PORT]); muint16LauncherPID = (quint16)atoi(parystrArgv[CLA_LAUNCHER_PID]); //Connect up the signals connect(this, SIGNAL(connected()), this, SLOT(onConnected())); connect(this, SIGNAL(disconnected()), this, SLOT(onDisconnected())); connect(this, SIGNAL(readyRead()), this, SLOT(onDataIn())); connect(this, &QAbstractSocket::errorOccurred, this, &clsModHelper::onErrorOccurred); //Module specific signals connect(this, &clsModHelper::terminateModule, this, &clsModHelper::onKillThreads); connect(this, &clsModHelper::threadTerminated, this, &clsModHelper::onExitModule); qdbg() << state(); //Connect to the Application connectToHost(clsModHelper::mscpszHost, muint16Port); if ( waitForConnected(clsModHelper::mscintConnectionTimeout) ) { //Set-up title strcpy(msszTitle, cpszTitle); //Install custom message handler clsDebugService::serviceDebugQueue(msszTitle, true, true); //Build up title for output to the console char szTitle[MODULE_NAME_LENGTH + 24]; sprintf(szTitle, "%s Version: %.2lf", cpszTitle, mdblVersion); qdbg() << szTitle; } }So now the base class prototype:
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); public slots: virtual void onConnected() = 0; virtual void onDataIn() = 0; virtual void onDisconnected() = 0; void onErrorOccurred(QAbstractSocket::SocketError socketError); void onExitModule(); void onKillThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };And the derived prototype:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } virtual void onConnected() override; virtual void onDataIn() override; virtual void onDisconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };With the code this way I get:
QAbstractSocket::UnconnectedState libc++abi.dylib: Pure virtual function called!In the Application Output.
-
@Bonnie I'll give that a try. To clarify here is the implementation:
clsModHelper::clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion) : QTcpSocket(pParent) , mblnExit(false) , mdblVersion(dblVersion) , mfpDbgLog(nullptr) , mint64AppPID(QCoreApplication::applicationPid()) , mpHeartbeat(nullptr), mpThread(nullptr) , muint16Port(0), muint16LauncherPID(0), muint16SleepTime(0) { if ( intArgc < CLA_LAUNCHER_PID ) { std::cerr << "Insufficient arguments, aborting!"; exit(EXIT_FAILURE); } if ( mspThis == nullptr ) { mspThis = this; } muint16Port = (quint16)atoi(parystrArgv[CLA_PORT]); muint16LauncherPID = (quint16)atoi(parystrArgv[CLA_LAUNCHER_PID]); //Connect up the signals connect(this, SIGNAL(connected()), this, SLOT(onConnected())); connect(this, SIGNAL(disconnected()), this, SLOT(onDisconnected())); connect(this, SIGNAL(readyRead()), this, SLOT(onDataIn())); connect(this, &QAbstractSocket::errorOccurred, this, &clsModHelper::onErrorOccurred); //Module specific signals connect(this, &clsModHelper::terminateModule, this, &clsModHelper::onKillThreads); connect(this, &clsModHelper::threadTerminated, this, &clsModHelper::onExitModule); qdbg() << state(); //Connect to the Application connectToHost(clsModHelper::mscpszHost, muint16Port); if ( waitForConnected(clsModHelper::mscintConnectionTimeout) ) { //Set-up title strcpy(msszTitle, cpszTitle); //Install custom message handler clsDebugService::serviceDebugQueue(msszTitle, true, true); //Build up title for output to the console char szTitle[MODULE_NAME_LENGTH + 24]; sprintf(szTitle, "%s Version: %.2lf", cpszTitle, mdblVersion); qdbg() << szTitle; } }So now the base class prototype:
class clsModHelper : public QTcpSocket { Q_OBJECT protected: bool mblnExit; double mdblVersion; FILE* mfpDbgLog; qint64 mint64AppPID; std::thread* mpHeartbeat; std::thread* mpThread; static const int mscintConnectionTimeout; static const int mscintHeartbeatFrequency; static const char* mscpszHost; static clsModHelper* mspThis; static char msszTitle[MODULE_NAME_LENGTH + 1]; quint16 muint16Port, muint16LauncherPID, muint16SleepTime; static bool blnToThisModule(clsJSON& objJSON); void cleanup(); void loopUntilExit(); void start(); public slots: virtual void onConnected() = 0; virtual void onDataIn() = 0; virtual void onDisconnected() = 0; void onErrorOccurred(QAbstractSocket::SocketError socketError); void onExitModule(); void onKillThreads(); public: static const char mscszAck[] ,mscszCmdHB[] ,mscszCommand[] ,mscszCommands[] ,mscszError[] ,mscszModule[] ,mscszMsgType[] ,mscszPID[] ,mscszResult[] ,mscszSkipOver[] ,mscszTime[] ,mscszTrue[] ,mscszType[] ,mscszTypeChar[] ,mscszTypeDouble[] ,mscszTypeFloat[] ,mscszTypeInt[] ,mscszTypeLong[] ,mscszTypeShort[] ,mscszTypeString[] ,mscszTypeUChar[] ,mscszTypeUInt[] ,mscszTypeULong[] ,mscszTypeUShort[]; explicit clsModHelper(QObject* pParent, int intArgc, char* parystrArgv[] ,const char* cpszTitle, double dblVersion); virtual ~clsModHelper(); double dblGetVersion() { return mdblVersion; } [[noreturn]] static void exitMsg(const char* cpszMsg ,const char* cpszPrefix = "ERROR" ,int intCode = EXIT_FAILURE); qint64 int64GetPID() { return mint64AppPID; } int intExitCode() { return 0; } static long lngTimeInSecs(); static clsModHelper* psGetModuleInstance() { return clsModHelper::mspThis; } static const char* cpszGetTitle() { return msszTitle; } void queueJSON(QJsonObject& objJSON); void sendHeartbeat(); void setSleepTime(quint16 uint16SleepTime) { muint16SleepTime = uint16SleepTime; } void threadBody(); virtual void moduleThreadBody() = 0; quint16 uint16GetLauncherPID() { return muint16LauncherPID; } signals: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); void terminateModule(); void threadTerminated(); };And the derived prototype:
class clsModFileIO : public clsModHelper { private: static clsModFileIO* mspInstance; void cmdLineSummary(); public: static const char mscszCheck[] ,mscszCmdFind[] ,mscszCmdRead[] ,mscszCmdSave[] ,mscszCmdTell[] ,mscszCmdWrite[] ,mscszByteOrder[] ,mscszData[] ,mscszFailed[] ,mscszFile[] ,mscszFrom[] ,mscszLength[] ,mscszMode[] ,mscszModeBinary[] ,mscszModeText[] ,mscszPattern[] ,mscszPosition[] ,mscszStart[] ,mscszSuccess[]; explicit clsModFileIO(QObject* pParent, int intArgc, char* parystrArgv[]); ~clsModFileIO(); bool doCommand(QString& strFile, QJsonObject& objRequest , QJsonObject& objCmd , QJsonObject& objResponse , quint16 uint16Idx = 0); void moduleThreadBody() override; static clsModFileIO* pInstance() { return mspInstance; } virtual void onConnected() override; virtual void onDataIn() override; virtual void onDisconnected() override; public slots: void onFailure(QJsonObject& objCmd); void onSuccess(QJsonObject& objCmd); };With the code this way I get:
QAbstractSocket::UnconnectedState libc++abi.dylib: Pure virtual function called!In the Application Output.