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

creating hash (or list) of member functions

Scheduled Pinned Locked Moved Solved C++ Gurus
30 Posts 5 Posters 4.7k 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.
  • Chris KawaC Chris Kawa

    @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);
    ...
    
    mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on 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!

    Chris KawaC JonBJ 2 Replies Last reply
    0
    • mzimmersM mzimmers has marked this topic as solved on
    • mzimmersM mzimmers

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

      Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by Chris Kawa
      #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.

      JonBJ 1 Reply Last reply
      1
      • mzimmersM mzimmers

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

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by JonB
        #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.HilkJ 1 Reply Last reply
        0
        • Chris KawaC Chris Kawa

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

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #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.

          Chris KawaC 1 Reply Last reply
          0
          • JonBJ JonB

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

            Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on 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.

            JonBJ 1 Reply Last reply
            1
            • Chris KawaC Chris Kawa

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

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #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.... :(

              Chris KawaC 1 Reply Last reply
              0
              • JonBJ JonB

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

                Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on 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.

                JonBJ 1 Reply Last reply
                1
                • Chris KawaC Chris Kawa

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

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by
                  #21

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

                  mzimmersM 1 Reply Last reply
                  0
                  • JonBJ JonB

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

                    mzimmersM Offline
                    mzimmersM Offline
                    mzimmers
                    wrote on 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
                    • JonBJ JonB

                      @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.HilkJ Offline
                      J.HilkJ Offline
                      J.Hilk
                      Moderators
                      wrote on 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.

                      JonBJ 1 Reply Last reply
                      1
                      • J.HilkJ J.Hilk

                        @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;}
                        };
                        
                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on 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"? ;-)

                        Chris KawaC 1 Reply Last reply
                        1
                        • JonBJ JonB

                          @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"? ;-)

                          Chris KawaC Offline
                          Chris KawaC Offline
                          Chris Kawa
                          Lifetime Qt Champion
                          wrote on 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.

                          JonBJ 1 Reply Last reply
                          2
                          • Chris KawaC Chris Kawa

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

                            JonBJ Offline
                            JonBJ Offline
                            JonB
                            wrote on 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.HilkJ 1 Reply Last reply
                            0
                            • JonBJ JonB

                              @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.HilkJ Offline
                              J.HilkJ Offline
                              J.Hilk
                              Moderators
                              wrote on 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.

                              JonBJ Chris KawaC 2 Replies Last reply
                              2
                              • J.HilkJ J.Hilk

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

                                JonBJ Offline
                                JonBJ Offline
                                JonB
                                wrote on 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.HilkJ J.Hilk

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

                                  Chris KawaC Offline
                                  Chris KawaC Offline
                                  Chris Kawa
                                  Lifetime Qt Champion
                                  wrote on 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

                                  JonBJ 1 Reply Last reply
                                  0
                                  • Chris KawaC Chris Kawa

                                    @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

                                    JonBJ Offline
                                    JonBJ Offline
                                    JonB
                                    wrote on 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

                                    • Login

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