Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Qt slots are not called in release mode and I blame QSharedPointer

Qt slots are not called in release mode and I blame QSharedPointer

Scheduled Pinned Locked Moved Solved General and Desktop
10 Posts 4 Posters 914 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Szymon. M. Sabat
    wrote on last edited by Szymon. M. Sabat
    #1

    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 of LANAssociation. TCPClient is child class to LANClient. LANClient has protected QSharedPointer<LANAssociation> connection variable, TCPClient has private QSharedPointer<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 of ThreadWorker. ThreadWorker::doWork() is virtual function. It was called in debug, not in release. I fixed it by making doWork() pure virtual - now it's called both in debug and release.

    I have also problem with QTcpSocket and QUdpSocket objects not emitting connected() or readyRead(). Now I understand it's probably because I am using QSharedPointer<QAbstractSocket> to manage them, but:

    1. why the difference between debug and release?
    2. how to do it properly? Should I stop using inheritance or shared pointers?
    3. why TCPAssociation::started() is not being emitted? LANClient::assoc_started is not a virtual function.
    sierdzioS KroMignonK 2 Replies Last reply
    0
    • S Szymon. M. Sabat

      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 of LANAssociation. TCPClient is child class to LANClient. LANClient has protected QSharedPointer<LANAssociation> connection variable, TCPClient has private QSharedPointer<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 of ThreadWorker. ThreadWorker::doWork() is virtual function. It was called in debug, not in release. I fixed it by making doWork() pure virtual - now it's called both in debug and release.

      I have also problem with QTcpSocket and QUdpSocket objects not emitting connected() or readyRead(). Now I understand it's probably because I am using QSharedPointer<QAbstractSocket> to manage them, but:

      1. why the difference between debug and release?
      2. how to do it properly? Should I stop using inheritance or shared pointers?
      3. why TCPAssociation::started() is not being emitted? LANClient::assoc_started is not a virtual function.
      sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by sierdzio
      #2

      @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 call connect() 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

      (Z(:^

      1 Reply Last reply
      13
      • sierdzioS Offline
        sierdzioS Offline
        sierdzio
        Moderators
        wrote on last edited by
        #3

        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
        

        (Z(:^

        VRoninV S 2 Replies Last reply
        2
        • S Szymon. M. Sabat

          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 of LANAssociation. TCPClient is child class to LANClient. LANClient has protected QSharedPointer<LANAssociation> connection variable, TCPClient has private QSharedPointer<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 of ThreadWorker. ThreadWorker::doWork() is virtual function. It was called in debug, not in release. I fixed it by making doWork() pure virtual - now it's called both in debug and release.

          I have also problem with QTcpSocket and QUdpSocket objects not emitting connected() or readyRead(). Now I understand it's probably because I am using QSharedPointer<QAbstractSocket> to manage them, but:

          1. why the difference between debug and release?
          2. how to do it properly? Should I stop using inheritance or shared pointers?
          3. why TCPAssociation::started() is not being emitted? LANClient::assoc_started is not a virtual function.
          KroMignonK Offline
          KroMignonK Offline
          KroMignon
          wrote on last edited by
          #4

          @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;
          }
          
          

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          1 Reply Last reply
          3
          • sierdzioS sierdzio

            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
            
            VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by
            #5

            @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

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            sierdzioS 1 Reply Last reply
            2
            • VRoninV VRonin

              @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

              sierdzioS Offline
              sierdzioS Offline
              sierdzio
              Moderators
              wrote on last edited by
              #6

              @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!

              (Z(:^

              1 Reply Last reply
              0
              • sierdzioS Offline
                sierdzioS Offline
                sierdzio
                Moderators
                wrote on last edited by
                #7

                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.

                (Z(:^

                1 Reply Last reply
                0
                • VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by
                  #8

                  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

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  sierdzioS 1 Reply Last reply
                  0
                  • VRoninV VRonin

                    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

                    sierdzioS Offline
                    sierdzioS Offline
                    sierdzio
                    Moderators
                    wrote on last edited by
                    #9

                    @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.

                    (Z(:^

                    1 Reply Last reply
                    2
                    • sierdzioS sierdzio

                      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
                      
                      S Offline
                      S Offline
                      Szymon. M. Sabat
                      wrote on last edited by
                      #10

                      @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
                      #endif

                      Thank 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.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved