Qt slots are not called in release mode and I blame QSharedPointer
-
I have a problem with signal-slot mechanism, may be that it's actually a problem with
QSharedPointer
as smart pointers are new to me.A little explanation of the context:
TCPAssociation
is child class ofLANAssociation
.TCPClient
is child class toLANClient
.LANClient
has protectedQSharedPointer<LANAssociation> connection
variable,TCPClient
has privateQSharedPointer<TCPAssociation> conn
; both pointing to the same object being created in overriden pure virtual function shown below:void TCPClient::startNewConnection(const QHostAddress &address) { conn.clear(); connection.clear(); conn = QSharedPointer<TCPAssociation>(new TCPAssociation(address, port, QString("Client %1").arg(hostId()))); Q_ASSERT(conn); Q_ASSERT(connect(conn.data(), &LANAssociation::started, this, &LANClient::assoc_started, Qt::UniqueConnection)); connection = conn.staticCast<LANAssociation>(); Q_ASSERT(connection); conn->start(); //TCPAssociation::start() { doStuff(); emit started(); } }
The code works on Windows 10 in debug mode just fine. In release mode on Linux,
LANClient::assoc_started
is never called.Now I ran my server application (above function is from a client app) in release on Windows and I can see another slot not being called in release but working just fine in debug:
void ThreadController::init(ThreadWorker *worker) { worker->moveToThread(&workerThread); Q_ASSERT(connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater, Qt::UniqueConnection)); Q_ASSERT(connect(this, &ThreadController::operate, worker, &ThreadWorker::doWork, Qt::UniqueConnection)); Q_ASSERT(connect(worker, &ThreadWorker::finished, this, &ThreadController::finished, Qt::BlockingQueuedConnection)); doConnects(worker); workerThread.start(); }
*worker
points to child class ofThreadWorker
.ThreadWorker::doWork()
is virtual function. It was called in debug, not in release. I fixed it by makingdoWork()
pure virtual - now it's called both in debug and release.I have also problem with
QTcpSocket
andQUdpSocket
objects not emittingconnected()
orreadyRead()
. Now I understand it's probably because I am usingQSharedPointer<QAbstractSocket>
to manage them, but:- why the difference between debug and release?
- how to do it properly? Should I stop using inheritance or shared pointers?
- why
TCPAssociation::started()
is not being emitted?LANClient::assoc_started
is not a virtual function.
-
@Szymon-M-Sabat said in Qt slots are not called in release mode and I blame QSharedPointer:
Q_ASSERT
Your problem is with
Q_ASSERT
, not with shared pointers. You callconnect()
inside an assert. In release mode, Q_ASSERT does nothing - your connection is not made at all! Here is assert code: https://code.woboq.org/qt5/qtbase/src/corelib/global/qglobal.h.html#837 -
If it helps, here is a
CHECK
macro I wrote for one of my projects. It's very simple, works same as assert in debug mode, but executes the condition in release. So it's just what you need ;-)#if !defined(CHECK) #if defined(DEBUG_BUILD) #define CHECK(condition) if (!condition) qFatal("Check failed!") #else #define CHECK(condition) condition #endif #endif
-
@Szymon-M-Sabat said in Qt slots are not called in release mode and I blame QSharedPointer:
Q_ASSERT(connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater, Qt::UniqueConnection));
As @sierdzio already say this is totally bad coding!!
Take a look at Debugging Macros:Q_ASSERT(), Q_ASSERT_X(), and Q_CHECK_PTR() expand to nothing if QT_NO_DEBUG is defined during compilation. For this reason, the arguments to these macro should not have any side-effects. Here is an incorrect usage of Q_CHECK_PTR():
char *alloc(int size) { char *ptr; Q_CHECK_PTR(ptr = new char[size]); // WRONG ==> removed in release buid!!! return ptr; // returns undefined value in release build! } char *alloc(int size) { char *ptr; ptr = new char[size]; Q_CHECK_PTR(ptr); // Removed in release build return ptr; }
-
@VRonin said in Qt slots are not called in release mode and I blame QSharedPointer:
@sierdzio said in Qt slots are not called in release mode and I blame QSharedPointer:
If it helps, here is a CHECK macro I wrote for one of my projects.
Qt already provides this, it's called
Q_ASSUME
:O and you tell me about it now? ;-)
Thanks!
-
Hm, reading the docs I think Q_ASSUME is different than my CHECK. It gives no guarantees.
CHECK will crash in debug if connection fails. In release, it will execute connect but won't do anything with the result.
So while developing you immediately know when connection (or anything else returning bool) fails, but in release you get smooth operation with no overhead.
-
@VRonin said in Qt slots are not called in release mode and I blame QSharedPointer:
Also, in this case you are using Qt5 connection syntax so there is no need to wrap it in Q_ASSUME() as it will be checked at compile time anyway
Compiler will check if arguments match. But it will not guarantee that a connection will be made. For example, if you connect same signals a few times, a syntactically correct (compiler is OK with it) statement can fail to connect.
Although I agree this is far more useful in other calls, like QMetaObject::invokeMethod(). Here it is super easy to miss some arguments.
-
@sierdzio said in Qt slots are not called in release mode and I blame QSharedPointer:
#if !defined(CHECK)
#if defined(DEBUG_BUILD)
#define CHECK(condition) if (!condition) qFatal("Check failed!")
#else
#define CHECK(condition) condition
#endif
#endifThank you, I will use that! I prefer it over Q_ASSUME too.
I was using Q_ASSERT on connect() just because sometimes I get warnings about my code but it won't tell me the exact line so I prefer for my app to just crash.