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 and coroutines
Forum Updated to NodeBB v4.3 + New Features

Qt and coroutines

Scheduled Pinned Locked Moved Unsolved General and Desktop
13 Posts 5 Posters 1.6k Views 3 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.
  • F Offline
    F Offline
    Fausto01
    wrote on 17 Oct 2023, 07:59 last edited by
    #1

    I have this class:

    my_class.hpp

    #ifndef MYCLASS_HPP
    #define MYCLASS_HPP
    
    #include <QObject>
    
    class MyClass : public QObject
    {
        Q_OBJECT
      public:
        MyClass(QObject* parent = nullptr);
    
        void doWork();
    
      signals:
        void workFinished();
    };
    
    #endif // MYCLASS_HPP
    

    And I need to achieve something like that:

    void function()
    {
      MyClass myClass;
    
      qDebug() << "Doing the work...";
      myClass.doWork();
      // waiting until the workFinished() signal is emitted
      doSomethig();
      qDebug() << "Work done";
    }
    

    Can I do that with coroutines? I don't want to fill the code with lambdas inside other lambdas.
    Thank you

    J 1 Reply Last reply 17 Oct 2023, 08:02
    0
    • F Fausto01
      17 Oct 2023, 07:59

      I have this class:

      my_class.hpp

      #ifndef MYCLASS_HPP
      #define MYCLASS_HPP
      
      #include <QObject>
      
      class MyClass : public QObject
      {
          Q_OBJECT
        public:
          MyClass(QObject* parent = nullptr);
      
          void doWork();
      
        signals:
          void workFinished();
      };
      
      #endif // MYCLASS_HPP
      

      And I need to achieve something like that:

      void function()
      {
        MyClass myClass;
      
        qDebug() << "Doing the work...";
        myClass.doWork();
        // waiting until the workFinished() signal is emitted
        doSomethig();
        qDebug() << "Work done";
      }
      

      Can I do that with coroutines? I don't want to fill the code with lambdas inside other lambdas.
      Thank you

      J Offline
      J Offline
      jsulm
      Lifetime Qt Champion
      wrote on 17 Oct 2023, 08:02 last edited by
      #2

      @Fausto01 I don't understand what the problem is.
      myClass.doWork() will block until it's done, so no need to "wait". Or is it going to be executed in another thread?

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      F 1 Reply Last reply 17 Oct 2023, 10:19
      0
      • J jsulm
        17 Oct 2023, 08:02

        @Fausto01 I don't understand what the problem is.
        myClass.doWork() will block until it's done, so no need to "wait". Or is it going to be executed in another thread?

        F Offline
        F Offline
        Fausto01
        wrote on 17 Oct 2023, 10:19 last edited by
        #3

        @jsulm The doWork() function runs async code, it's non blocking

        J 1 Reply Last reply 17 Oct 2023, 10:25
        0
        • F Fausto01
          17 Oct 2023, 10:19

          @jsulm The doWork() function runs async code, it's non blocking

          J Offline
          J Offline
          jsulm
          Lifetime Qt Champion
          wrote on 17 Oct 2023, 10:25 last edited by
          #4

          @Fausto01 Then use signals and slots, no need to wait inside function().
          Connect a slot to workFinished() signal and call doSomething() there (or make doSomething() a slot and connect it directly to workFinished()).

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          F 1 Reply Last reply 17 Oct 2023, 14:32
          1
          • J jsulm
            17 Oct 2023, 10:25

            @Fausto01 Then use signals and slots, no need to wait inside function().
            Connect a slot to workFinished() signal and call doSomething() there (or make doSomething() a slot and connect it directly to workFinished()).

            F Offline
            F Offline
            Fausto01
            wrote on 17 Oct 2023, 14:32 last edited by
            #5

            @jsulm I know I could use signal and slots, but I wanted to use coroutines so that my code would be more linear

            C 1 Reply Last reply 17 Oct 2023, 17:33
            0
            • F Fausto01
              17 Oct 2023, 14:32

              @jsulm I know I could use signal and slots, but I wanted to use coroutines so that my code would be more linear

              C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 17 Oct 2023, 17:33 last edited by
              #6

              @Fausto01 Sounds like you want to use coroutines because you want to use coroutines. Is it really worth it?

              Anyway, it's not very Qt style, but if you really want to wait in the function until a signal comes just use local event loop. No lambdas or coroutines required.

              MyClass myClass;
              myClass.doWork();
              
              QEventLoop loop;
              connect(&myClass, &MyClass::workFinished, &loop, &QEventLoop::quit);
              loop.exec();
              
              doSomethig();
              

              You can wrap it in some function, so it's easier to reuse, e.g.

              MyClass myClass;
              myClass.doWork();
              
              waitFor(&myClass, &MyClass::workFinished);
              
              doSomethig();
              

              Answering your question directly - coroutines on their own do not specify if they run on the same or another thread. that's up to the implementation of the task class. You'd have to have support structures that would run the coroutine in another thread and wake the caller in time to pump the message queue and process the signal. That's doable, but nothing like that exists out of the box in Qt, so you'd have to implement that yourself. Sounds to me like a whole can of worms for no gain.

              F 1 Reply Last reply 17 Oct 2023, 18:13
              0
              • C Chris Kawa
                17 Oct 2023, 17:33

                @Fausto01 Sounds like you want to use coroutines because you want to use coroutines. Is it really worth it?

                Anyway, it's not very Qt style, but if you really want to wait in the function until a signal comes just use local event loop. No lambdas or coroutines required.

                MyClass myClass;
                myClass.doWork();
                
                QEventLoop loop;
                connect(&myClass, &MyClass::workFinished, &loop, &QEventLoop::quit);
                loop.exec();
                
                doSomethig();
                

                You can wrap it in some function, so it's easier to reuse, e.g.

                MyClass myClass;
                myClass.doWork();
                
                waitFor(&myClass, &MyClass::workFinished);
                
                doSomethig();
                

                Answering your question directly - coroutines on their own do not specify if they run on the same or another thread. that's up to the implementation of the task class. You'd have to have support structures that would run the coroutine in another thread and wake the caller in time to pump the message queue and process the signal. That's doable, but nothing like that exists out of the box in Qt, so you'd have to implement that yourself. Sounds to me like a whole can of worms for no gain.

                F Offline
                F Offline
                Fausto01
                wrote on 17 Oct 2023, 18:13 last edited by
                #7

                @Chris-Kawa yeah I've tried the QEventLoop way, but sometimes it reprocesses the same event multiple times, I think that's because I used the event loop inside a slot from QML. Is it possible?

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 17 Oct 2023, 18:39 last edited by
                  #8

                  Hi,

                  Can you explain what you want to achieve ?
                  From the outside, it seems you want to do some blocking parallelism which usually defeats the purpose of parallelism.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  F 2 Replies Last reply 17 Oct 2023, 19:38
                  0
                  • S SGaist
                    17 Oct 2023, 18:39

                    Hi,

                    Can you explain what you want to achieve ?
                    From the outside, it seems you want to do some blocking parallelism which usually defeats the purpose of parallelism.

                    F Offline
                    F Offline
                    Fausto01
                    wrote on 17 Oct 2023, 19:38 last edited by
                    #9

                    @SGaist my bad if I did not explain it clearly, my goal is to write the code as if it was synchronous, but it is asynchronous instead.
                    I took inspiration from QCoro library but I cannot use it.
                    I want the code to be linear as the QEvenLoop example, but I would like to not use it.

                    1 Reply Last reply
                    0
                    • S SGaist
                      17 Oct 2023, 18:39

                      Hi,

                      Can you explain what you want to achieve ?
                      From the outside, it seems you want to do some blocking parallelism which usually defeats the purpose of parallelism.

                      F Offline
                      F Offline
                      Fausto01
                      wrote on 17 Oct 2023, 19:40 last edited by
                      #10

                      @SGaist something like that, where the get() function runs asynchronously

                      QNetworkAccessManager networkAccessManager;
                      // co_await the reply - the coroutine is suspended until the QNetworkReply is finished.
                      // While the coroutine is suspended, *the Qt event loop runs as usual*.
                      const QNetworkReply *reply = co_await networkAccessManager.get(url);
                      // Once the reply is finished, your code resumes here as if nothing amazing has just happened ;-)
                      const auto data = reply->readAll();
                      
                      C S 2 Replies Last reply 17 Oct 2023, 20:56
                      0
                      • F Fausto01
                        17 Oct 2023, 19:40

                        @SGaist something like that, where the get() function runs asynchronously

                        QNetworkAccessManager networkAccessManager;
                        // co_await the reply - the coroutine is suspended until the QNetworkReply is finished.
                        // While the coroutine is suspended, *the Qt event loop runs as usual*.
                        const QNetworkReply *reply = co_await networkAccessManager.get(url);
                        // Once the reply is finished, your code resumes here as if nothing amazing has just happened ;-)
                        const auto data = reply->readAll();
                        
                        C Offline
                        C Offline
                        Chris Kawa
                        Lifetime Qt Champion
                        wrote on 17 Oct 2023, 20:56 last edited by Chris Kawa
                        #11

                        @Fausto01 The problem is that you can't just suspend the execution if you're in the main thread. It needs to pump the event loop or your app will freeze. If you're not in the main thread you can suspend, but you still need an active event loop to process the finish signal and if you're blocking you might as well just block explicitly and not use coroutines.

                        I think you need to explain your use case a bit more. Where do you start the "task" - main thread or a worker. What do you want to happen with that thread - do you want it to block or keep processing events.

                        In general Qt model is event based - it needs an active message pump that processes incoming signals or system events. Coroutines are not really suited for that I think. Sure you can force any code into using them, but it kinda works against the design of the library. "I want the code to be linear" is not a good idea when the library is not designed to be used like that. You're not gonna get better code forcing the library into a pattern it wasn't designed for.

                        I've tried the QEventLoop way, but sometimes it reprocesses the same event multiple times,

                        That... doesn't sound plausible. There's really no magic to it. It's just a simple message queue. More likely you had a bug in your code, but I can't say for sure without more details.

                        1 Reply Last reply
                        3
                        • F Fausto01
                          17 Oct 2023, 19:40

                          @SGaist something like that, where the get() function runs asynchronously

                          QNetworkAccessManager networkAccessManager;
                          // co_await the reply - the coroutine is suspended until the QNetworkReply is finished.
                          // While the coroutine is suspended, *the Qt event loop runs as usual*.
                          const QNetworkReply *reply = co_await networkAccessManager.get(url);
                          // Once the reply is finished, your code resumes here as if nothing amazing has just happened ;-)
                          const auto data = reply->readAll();
                          
                          S Offline
                          S Offline
                          SimonSchroeder
                          wrote on 18 Oct 2023, 07:39 last edited by
                          #12

                          @Fausto01 said in Qt and coroutines:

                          something like that, where the get() function runs asynchronously

                          I guess you need to further define what you mean with "asynchronously". Your example shows network communication. One of the best examples for this sort of asynchronicity is the asio lib. Another good example is signals/slots in Qt (yes, this is called asynchronous!). Both of these don't allow for a linear flow of logic in your code.

                          C++'s coroutines are still in its infancy. I do understand that it would be nice to use co_await wherever you are in your code. It is impossible to use Qt's classes, like QNetworkAccessManager, directly with coroutines. You would have to write your own class and wrap this. It does not seem to be possible to generalize coroutines for anything else. What would have to happen for the QNetworkAccessManager is something like this:

                          1. Call QNetworkAccessManager::get(url).
                          2. Create a local event loop.
                          3. Connect QNetworkReply::finished() to quit the local event loop.
                          4. Run the local event loop.

                          Certainly the local event loop part can be generalized, but connecting the right signal to finish the event loop can not.

                          Maybe, this way of thinking (in the context of Qt) is also totally backwards. Your own function (creating a QNetworkAccessManager, getting a QNetworkReply, reading the data) should be a coroutine itself. After calling QNetworkAccessManager::get() you would co_yield to return to the main event loop. But before that you need to connect QNetworkReply::finished() to a slot which will then call your coroutine again in order to continue after the co_yield. This would truly allow for linear code on your side. This approach should work with a generalized coroutine which handles interfacing the coroutine with the Qt event loop. The only thing left on the programmers side would be to connect a single signal to your own generalized coroutine's slot which would resume the coroutine.

                          1 Reply Last reply
                          0
                          • F Offline
                            F Offline
                            Fausto01
                            wrote on 18 Oct 2023, 08:29 last edited by
                            #13

                            Thank you for all your support guys! Everything is much more clear now!

                            1 Reply Last reply
                            0

                            2/13

                            17 Oct 2023, 08:02

                            topic:navigator.unread, 11
                            • Login

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