Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. creating hash (or list) of member functions
QtWS25 Last Chance

creating hash (or list) of member functions

Scheduled Pinned Locked Moved Solved C++ Gurus
30 Posts 5 Posters 4.0k Views
  • 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.
  • M mzimmers
    10 May 2023, 14:49

    @Chris-Kawa @JonB OK, so an iterator is a bad idea here. So, I guess I need a member index instead, and I'll just manually keep track of where in the list I am?

    EDIT:

    I'm back to implementing Chris' first suggestion, but I need a way to go through the hash in a round-robin manner. If an iterator isn't recommended, how do I do this? Maybe a QHash isn't the right container if all the values are the same.

    In other words:

    void Timer::notifyClient()
    {
        QObject *client; // how do I know which QHash element to use?
        QMetaObject::invokeMethod(client, m_clients[client].data());
    }
    

    Thanks...

    C Offline
    C Offline
    Chris Kawa
    Lifetime Qt Champion
    wrote on 10 May 2023, 17:11 last edited by
    #11

    @mzimmers Sorry, it's not clear to me what you want to achieve. The way you have it set up you can have multiple clients registered for each timer separately. When a single timer times out do you want to notify all clients of all timers, all clients of one timer, one client from all the timers or one client from one timer?

    You can't use any statics if you want to handle each timer separately, as static variables are shared between instances. If you want to notify one client for one timer going through the list each time out then don't use hash. Put clients in a list and keep an index. Just make sure you don't go out of range if a client unregisters and the list shrinks.

    M 1 Reply Last reply 10 May 2023, 17:21
    1
    • C Chris Kawa
      10 May 2023, 17:11

      @mzimmers Sorry, it's not clear to me what you want to achieve. The way you have it set up you can have multiple clients registered for each timer separately. When a single timer times out do you want to notify all clients of all timers, all clients of one timer, one client from all the timers or one client from one timer?

      You can't use any statics if you want to handle each timer separately, as static variables are shared between instances. If you want to notify one client for one timer going through the list each time out then don't use hash. Put clients in a list and keep an index. Just make sure you don't go out of range if a client unregisters and the list shrinks.

      M Offline
      M Offline
      mzimmers
      wrote on 10 May 2023, 17:21 last edited by mzimmers 5 Oct 2023, 17:22
      #12

      @Chris-Kawa the plan was to have a list of items that require periodic updates. The Timer class would keep a list of these items, and upon timer expiry, would notify one of the items in the list. The intention was to avoid doing all of the updates at once (to spread out the resource utilization).

      If I use a list, what does the call to the update method look like? I'm having trouble properly forming it. I'm guessing that I can't just keep a list of the callbacks, right - I need the object as well?

      C 1 Reply Last reply 10 May 2023, 17:38
      0
      • M mzimmers
        10 May 2023, 17:21

        @Chris-Kawa the plan was to have a list of items that require periodic updates. The Timer class would keep a list of these items, and upon timer expiry, would notify one of the items in the list. The intention was to avoid doing all of the updates at once (to spread out the resource utilization).

        If I use a list, what does the call to the update method look like? I'm having trouble properly forming it. I'm guessing that I can't just keep a list of the callbacks, right - I need the object as well?

        C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 10 May 2023, 17:38 last edited by Chris Kawa 5 Oct 2023, 17:39
        #13

        @mzimmers said:

        If I use a list, what does the call to the update method look like?

        For example like this:

        class Timer : public QObject
        {
            Q_OBJECT
        
            QList<std::function<void()>> clients;
            int client_index = -1;
        
        public:
            template<typename T>
            void registerClient(T* client, void(T::*cs)())
            {
                clients.push_back(std::bind(cs, client));
            }
        
            void notifyTimer()
            {
                if (!clients.isEmpty())
                {
                    if (++client_index >= clients.size())
                        client_index = 0;
        
                    clients[client_index]();
                }
            }
        };
        

        and you register clients like this:

        timer.registerClient(clock1, &Clock::update);
        timer.registerClient(clock2, &Clock::update);
        ...
        
        M 1 Reply Last reply 10 May 2023, 18:11
        4
        • C Chris Kawa
          10 May 2023, 17:38

          @mzimmers said:

          If I use a list, what does the call to the update method look like?

          For example like this:

          class Timer : public QObject
          {
              Q_OBJECT
          
              QList<std::function<void()>> clients;
              int client_index = -1;
          
          public:
              template<typename T>
              void registerClient(T* client, void(T::*cs)())
              {
                  clients.push_back(std::bind(cs, client));
              }
          
              void notifyTimer()
              {
                  if (!clients.isEmpty())
                  {
                      if (++client_index >= clients.size())
                          client_index = 0;
          
                      clients[client_index]();
                  }
              }
          };
          

          and you register clients like this:

          timer.registerClient(clock1, &Clock::update);
          timer.registerClient(clock2, &Clock::update);
          ...
          
          M Offline
          M Offline
          mzimmers
          wrote on 10 May 2023, 18:11 last edited by
          #14

          @Chris-Kawa yeah, I'd actually gotten something working, but I like yours better, because it accepts the callback as an argument, rather than hardcoding it in the timer.

          I think this topic is closed, but I wonder if you could give me an explanation for the use of the std::bind. The list is just a list of QObjects; how does this "attach" the callback function?

          Thanks!

          C J 2 Replies Last reply 10 May 2023, 18:36
          0
          • M mzimmers has marked this topic as solved on 10 May 2023, 18:11
          • M mzimmers
            10 May 2023, 18:11

            @Chris-Kawa yeah, I'd actually gotten something working, but I like yours better, because it accepts the callback as an argument, rather than hardcoding it in the timer.

            I think this topic is closed, but I wonder if you could give me an explanation for the use of the std::bind. The list is just a list of QObjects; how does this "attach" the callback function?

            Thanks!

            C Offline
            C Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 10 May 2023, 18:36 last edited by Chris Kawa 5 Oct 2023, 18:39
            #15

            @mzimmers In my example it's not a list of QObjects. It's a list of std::function objects.
            Clock::update() is a class member function, so to call it you need an instance of that class i.e. instance->update(). The way class member functions work is that they really are just regular functions that have a hidden implicit this parameter, so in effect it's like Clock::update(instance).
            std::bind, as the name suggests, creates a callable object that binds a functor with a parameter, so you can call it as if there was no parameter.
            The way to think about it is that std::bind(cs, client) creates a struct with the operator(), something like this:

            struct Something
            {
               Clock* client;
               void operator()() { client->update(); }
            }
            

            so it turns a class member function with hidden this parameter into something that can be called without parameters. Then I just store it in a std::function object that can hold any type of callables (functions, functors, lambdas etc.).
            In other words std::bind creates something that holds information about both object and a function pointer, so you don't need anything extra to call it.

            J 1 Reply Last reply 10 May 2023, 18:49
            1
            • M mzimmers
              10 May 2023, 18:11

              @Chris-Kawa yeah, I'd actually gotten something working, but I like yours better, because it accepts the callback as an argument, rather than hardcoding it in the timer.

              I think this topic is closed, but I wonder if you could give me an explanation for the use of the std::bind. The list is just a list of QObjects; how does this "attach" the callback function?

              Thanks!

              J Offline
              J Offline
              JonB
              wrote on 10 May 2023, 18:45 last edited by JonB 5 Oct 2023, 18:46
              #16

              @mzimmers said in creating hash (or list) of member functions:

              but I wonder if you could give me an explanation for the use of the std::bind. The list is just a list of QObjects; how does this "attach" the callback function?

              All of this in place of the typedef void (*clientSlot)(), with C life used to be so simple :) We can't use that to call a C++ class member function on an instance. So...

              std::function<void()>
              

              I can be used to call a C++ class member method.

              registerClient(T* client, void(T::*cs)())
              

              Here's my client object (of a certain type), and here is the class member function.

              clients.push_back(std::bind(cs, client));
              

              Creates and pushes an object which, when invoked, will call cs(client). Which turns out to be the same as client->cs(). Which I am just about to question @Chris-Kawa on...!

              J 1 Reply Last reply 11 May 2023, 06:00
              0
              • C Chris Kawa
                10 May 2023, 18:36

                @mzimmers In my example it's not a list of QObjects. It's a list of std::function objects.
                Clock::update() is a class member function, so to call it you need an instance of that class i.e. instance->update(). The way class member functions work is that they really are just regular functions that have a hidden implicit this parameter, so in effect it's like Clock::update(instance).
                std::bind, as the name suggests, creates a callable object that binds a functor with a parameter, so you can call it as if there was no parameter.
                The way to think about it is that std::bind(cs, client) creates a struct with the operator(), something like this:

                struct Something
                {
                   Clock* client;
                   void operator()() { client->update(); }
                }
                

                so it turns a class member function with hidden this parameter into something that can be called without parameters. Then I just store it in a std::function object that can hold any type of callables (functions, functors, lambdas etc.).
                In other words std::bind creates something that holds information about both object and a function pointer, so you don't need anything extra to call it.

                J Offline
                J Offline
                JonB
                wrote on 10 May 2023, 18:49 last edited by JonB 5 Oct 2023, 18:54
                #17

                @Chris-Kawa said in creating hash (or list) of member functions:

                The way class member functions work is that they really are just regular functions that have a hidden implicit this parameter, so in effect it's like Clock::update(instance).

                OMG! But where does C++ tell you this and that you can write code to use it? I had no idea this was "documented" or "supported". I assumed implementation was opaque/abstract.

                C 1 Reply Last reply 10 May 2023, 18:59
                0
                • J JonB
                  10 May 2023, 18:49

                  @Chris-Kawa said in creating hash (or list) of member functions:

                  The way class member functions work is that they really are just regular functions that have a hidden implicit this parameter, so in effect it's like Clock::update(instance).

                  OMG! But where does C++ tell you this and that you can write code to use it? I had no idea this was "documented" or "supported". I assumed implementation was opaque/abstract.

                  C Offline
                  C Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on 10 May 2023, 18:59 last edited by
                  #18

                  @JonB said:

                  But where does C++ tell you this and that you can write code to use it?

                  Well no, you can't currently write it like that. I meant it conceptually. That's just what the compiler does anyway (you can see it e.g. in the mangled function signatures when inspecting C++ library exports).

                  Although the so called Uniform Call Syntax has been proposed multiple times over the years, including by Mr. C++ himself: N4474, so you might see it in some future standard version.

                  J 1 Reply Last reply 10 May 2023, 19:56
                  1
                  • C Chris Kawa
                    10 May 2023, 18:59

                    @JonB said:

                    But where does C++ tell you this and that you can write code to use it?

                    Well no, you can't currently write it like that. I meant it conceptually. That's just what the compiler does anyway (you can see it e.g. in the mangled function signatures when inspecting C++ library exports).

                    Although the so called Uniform Call Syntax has been proposed multiple times over the years, including by Mr. C++ himself: N4474, so you might see it in some future standard version.

                    J Offline
                    J Offline
                    JonB
                    wrote on 10 May 2023, 19:56 last edited by JonB 5 Oct 2023, 19:57
                    #19

                    @Chris-Kawa said in creating hash (or list) of member functions:

                    Well no, you can't currently write it like that. I meant it conceptually.

                    Oh, right! For a while there I thought you were saying literally.

                    I suppose I ought go look at what magic std::bind() actually does, then it would be clear. But I just know it's going to look complicated.... :(

                    C 1 Reply Last reply 10 May 2023, 20:24
                    0
                    • J JonB
                      10 May 2023, 19:56

                      @Chris-Kawa said in creating hash (or list) of member functions:

                      Well no, you can't currently write it like that. I meant it conceptually.

                      Oh, right! For a while there I thought you were saying literally.

                      I suppose I ought go look at what magic std::bind() actually does, then it would be clear. But I just know it's going to look complicated.... :(

                      C Offline
                      C Offline
                      Chris Kawa
                      Lifetime Qt Champion
                      wrote on 10 May 2023, 20:24 last edited by
                      #20

                      @JonB said:

                      But I just know it's going to look complicated.... :(

                      It does look a bit complicated, but it has to deal with variable number of perfectly forwarded arguments and a lot of weird corner cases users come up with. Also it's the standard library, so it's mangled with all those underscore names and defensive programming style, but if you squint a little you'll see it basically returns a class with operator() like I mentioned.

                      J 1 Reply Last reply 10 May 2023, 21:19
                      1
                      • C Chris Kawa
                        10 May 2023, 20:24

                        @JonB said:

                        But I just know it's going to look complicated.... :(

                        It does look a bit complicated, but it has to deal with variable number of perfectly forwarded arguments and a lot of weird corner cases users come up with. Also it's the standard library, so it's mangled with all those underscore names and defensive programming style, but if you squint a little you'll see it basically returns a class with operator() like I mentioned.

                        J Offline
                        J Offline
                        JonB
                        wrote on 10 May 2023, 21:19 last edited by
                        #21

                        @Chris-Kawa
                        Thanks. You gotta love hardcore C++, it's so... simple and clean.

                        M 1 Reply Last reply 10 May 2023, 21:21
                        0
                        • J JonB
                          10 May 2023, 21:19

                          @Chris-Kawa
                          Thanks. You gotta love hardcore C++, it's so... simple and clean.

                          M Offline
                          M Offline
                          mzimmers
                          wrote on 10 May 2023, 21:21 last edited by
                          #22

                          @JonB said in creating hash (or list) of member functions:

                          @Chris-Kawa
                          Thanks. You gotta love hardcore C++, it's so... simple and clean.

                          Now, now...no sarcasm.

                          But yeah...wouldn't you love to have today's compute resources available for solving the problems of 30 years ago?

                          1 Reply Last reply
                          0
                          • J JonB
                            10 May 2023, 18:45

                            @mzimmers said in creating hash (or list) of member functions:

                            but I wonder if you could give me an explanation for the use of the std::bind. The list is just a list of QObjects; how does this "attach" the callback function?

                            All of this in place of the typedef void (*clientSlot)(), with C life used to be so simple :) We can't use that to call a C++ class member function on an instance. So...

                            std::function<void()>
                            

                            I can be used to call a C++ class member method.

                            registerClient(T* client, void(T::*cs)())
                            

                            Here's my client object (of a certain type), and here is the class member function.

                            clients.push_back(std::bind(cs, client));
                            

                            Creates and pushes an object which, when invoked, will call cs(client). Which turns out to be the same as client->cs(). Which I am just about to question @Chris-Kawa on...!

                            J Offline
                            J Offline
                            J.Hilk
                            Moderators
                            wrote on 11 May 2023, 06:00 last edited by
                            #23

                            @JonB said in creating hash (or list) of member functions:

                            All of this in place of the typedef void (*clientSlot)(), with C life used to be so simple :) We can't use that to call a C++ class member function on an instance

                            who says you can't ?

                            #include <array>
                            
                            class SomeClass : public QObject
                            {
                                Q_OBJECT
                                typedef void (SomeClass::*SomeClassFunction)();
                            
                                std::array<SomeClassFunction,3> arrayOfSignalsPointers{&SomeClass::signal1,&SomeClass::signal2, &SomeClass::signal3};
                                std::array<SomeClassFunction, 3> arrayOfSlotsPointers{&SomeClass::slot1, &SomeClass::slot2, &SomeClass::slot3};
                            
                            public:
                                explicit SomeClass(QObject *parent = nullptr) : QObject(parent)
                                {
                                    QObject::connect(this, &SomeClass::signal1, this, &SomeClass::slot1);
                                    QObject::connect(this, &SomeClass::signal2, this, &SomeClass::slot2);
                                    QObject::connect(this, &SomeClass::signal3, this, &SomeClass::slot3);
                            
                                    qDebug() << "Emit all signals";
                                    for(auto entry : arrayOfSignalsPointers){
                                        (this->*entry)();
                                    }
                            
                                    qDebug() << "Call all slots directly";
                                    for(auto entry : arrayOfSlotsPointers){
                                        (this->*entry)();
                                    }
                                }
                            
                            signals:
                                void signal1();
                                void signal2();
                                void signal3();
                            
                            public slots:
                                void slot1(){qDebug() <<  Q_FUNC_INFO;}
                                void slot2(){qDebug() <<  Q_FUNC_INFO;}
                                void slot3(){qDebug() <<  Q_FUNC_INFO;}
                            };
                            

                            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                            Q: What's that?
                            A: It's blue light.
                            Q: What does it do?
                            A: It turns blue.

                            J 1 Reply Last reply 11 May 2023, 06:40
                            1
                            • J J.Hilk
                              11 May 2023, 06:00

                              @JonB said in creating hash (or list) of member functions:

                              All of this in place of the typedef void (*clientSlot)(), with C life used to be so simple :) We can't use that to call a C++ class member function on an instance

                              who says you can't ?

                              #include <array>
                              
                              class SomeClass : public QObject
                              {
                                  Q_OBJECT
                                  typedef void (SomeClass::*SomeClassFunction)();
                              
                                  std::array<SomeClassFunction,3> arrayOfSignalsPointers{&SomeClass::signal1,&SomeClass::signal2, &SomeClass::signal3};
                                  std::array<SomeClassFunction, 3> arrayOfSlotsPointers{&SomeClass::slot1, &SomeClass::slot2, &SomeClass::slot3};
                              
                              public:
                                  explicit SomeClass(QObject *parent = nullptr) : QObject(parent)
                                  {
                                      QObject::connect(this, &SomeClass::signal1, this, &SomeClass::slot1);
                                      QObject::connect(this, &SomeClass::signal2, this, &SomeClass::slot2);
                                      QObject::connect(this, &SomeClass::signal3, this, &SomeClass::slot3);
                              
                                      qDebug() << "Emit all signals";
                                      for(auto entry : arrayOfSignalsPointers){
                                          (this->*entry)();
                                      }
                              
                                      qDebug() << "Call all slots directly";
                                      for(auto entry : arrayOfSlotsPointers){
                                          (this->*entry)();
                                      }
                                  }
                              
                              signals:
                                  void signal1();
                                  void signal2();
                                  void signal3();
                              
                              public slots:
                                  void slot1(){qDebug() <<  Q_FUNC_INFO;}
                                  void slot2(){qDebug() <<  Q_FUNC_INFO;}
                                  void slot3(){qDebug() <<  Q_FUNC_INFO;}
                              };
                              
                              J Offline
                              J Offline
                              JonB
                              wrote on 11 May 2023, 06:40 last edited by
                              #24

                              @J-Hilk said in creating hash (or list) of member functions:

                              typedef void (SomeClass::*SomeClassFunction)();

                              I said that you cannot use typedef void (*clientSlot)();, as the OP wrote and one would in C, to call a C++ member function. And you can't: as you show you need ClassName::*function not just plain *function.

                              Having said that, I was nonetheless not aware that you can use that to get a member function's address and then call (instance->*memberFunctionPointer)(). Thank you for clarifying.

                              So.... all this std::function<> and particularly std::bind() looks like the usual C++ "why would you want to write something simple when you can wrap it up to be complicated"? ;-)

                              C 1 Reply Last reply 11 May 2023, 06:56
                              1
                              • J JonB
                                11 May 2023, 06:40

                                @J-Hilk said in creating hash (or list) of member functions:

                                typedef void (SomeClass::*SomeClassFunction)();

                                I said that you cannot use typedef void (*clientSlot)();, as the OP wrote and one would in C, to call a C++ member function. And you can't: as you show you need ClassName::*function not just plain *function.

                                Having said that, I was nonetheless not aware that you can use that to get a member function's address and then call (instance->*memberFunctionPointer)(). Thank you for clarifying.

                                So.... all this std::function<> and particularly std::bind() looks like the usual C++ "why would you want to write something simple when you can wrap it up to be complicated"? ;-)

                                C Offline
                                C Offline
                                Chris Kawa
                                Lifetime Qt Champion
                                wrote on 11 May 2023, 06:56 last edited by
                                #25

                                @JonB said:

                                So.... all this std::function<> and particularly std::bind() looks like the usual C++ "why would you want to write something simple when you can wrap it up to be complicated"? ;-)

                                No, it's a way to be generic. To write typedef void (SomeClass::*SomeClassFunction)() you have to hardcode SomeClass i.e. know it up front. Notice that what @J-Hilk posted will work with one particular class only. I know he just shows how to call a member function from a pointer and that's fine, but it doesn't do much for the original problem.
                                std::function doesn't care. It just takes any functor you give it and std::bind creates a functor from anything callable you give it.

                                J 1 Reply Last reply 11 May 2023, 07:01
                                2
                                • C Chris Kawa
                                  11 May 2023, 06:56

                                  @JonB said:

                                  So.... all this std::function<> and particularly std::bind() looks like the usual C++ "why would you want to write something simple when you can wrap it up to be complicated"? ;-)

                                  No, it's a way to be generic. To write typedef void (SomeClass::*SomeClassFunction)() you have to hardcode SomeClass i.e. know it up front. Notice that what @J-Hilk posted will work with one particular class only. I know he just shows how to call a member function from a pointer and that's fine, but it doesn't do much for the original problem.
                                  std::function doesn't care. It just takes any functor you give it and std::bind creates a functor from anything callable you give it.

                                  J Offline
                                  J Offline
                                  JonB
                                  wrote on 11 May 2023, 07:01 last edited by
                                  #26

                                  @Chris-Kawa
                                  Ah yes, I get it.

                                  Modern C++ programming is hugely about templates. But, correct me if I am wrong, C++ did not start out with templates, did it?

                                  J 1 Reply Last reply 11 May 2023, 07:05
                                  0
                                  • J JonB
                                    11 May 2023, 07:01

                                    @Chris-Kawa
                                    Ah yes, I get it.

                                    Modern C++ programming is hugely about templates. But, correct me if I am wrong, C++ did not start out with templates, did it?

                                    J Offline
                                    J Offline
                                    J.Hilk
                                    Moderators
                                    wrote on 11 May 2023, 07:05 last edited by
                                    #27

                                    @JonB proposed in 1988, realised in 1990 so a decade after the "initial commit" :P


                                    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                                    Q: What's that?
                                    A: It's blue light.
                                    Q: What does it do?
                                    A: It turns blue.

                                    J C 2 Replies Last reply 11 May 2023, 07:10
                                    2
                                    • J J.Hilk
                                      11 May 2023, 07:05

                                      @JonB proposed in 1988, realised in 1990 so a decade after the "initial commit" :P

                                      J Offline
                                      J Offline
                                      JonB
                                      wrote on 11 May 2023, 07:10 last edited by
                                      #28

                                      @J-Hilk Ah, thanks, that is much earlier than I realized, I thought more like 10 years later.

                                      1 Reply Last reply
                                      0
                                      • J J.Hilk
                                        11 May 2023, 07:05

                                        @JonB proposed in 1988, realised in 1990 so a decade after the "initial commit" :P

                                        C Offline
                                        C Offline
                                        Chris Kawa
                                        Lifetime Qt Champion
                                        wrote on 11 May 2023, 07:12 last edited by
                                        #29

                                        @J-Hilk Where "initial commit" at that time would probably be Stroustrup saving it to a big floppy and physically carrying it to the cubicle of his coworkers. Good old times :D

                                        J 1 Reply Last reply 11 May 2023, 07:18
                                        0
                                        • C Chris Kawa
                                          11 May 2023, 07:12

                                          @J-Hilk Where "initial commit" at that time would probably be Stroustrup saving it to a big floppy and physically carrying it to the cubicle of his coworkers. Good old times :D

                                          J Offline
                                          J Offline
                                          JonB
                                          wrote on 11 May 2023, 07:18 last edited by
                                          #30

                                          @Chris-Kawa

                                          We were beyond floppies by then. The first computer I used had 10.5 inch (I think, unless it was only 8 inch) floppies, https://www.computinghistory.org.uk/det/10247/Nord-ND305-355-Floppy-Disk/ :)

                                          1 Reply Last reply
                                          0

                                          20/30

                                          10 May 2023, 20:24

                                          • Login

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