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. template function with signal as parameter
Forum Updated to NodeBB v4.3 + New Features

template function with signal as parameter

Scheduled Pinned Locked Moved Solved C++ Gurus
19 Posts 5 Posters 3.0k 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.
  • Christian EhrlicherC Christian Ehrlicher

    @JonB said in template function with signal as parameter:

    telling you to call with a lambda

    Yes, since my first post but I'm getting ignored...

    Christian EhrlicherC Offline
    Christian EhrlicherC Offline
    Christian Ehrlicher
    Lifetime Qt Champion
    wrote on last edited by Christian Ehrlicher
    #10

    And if you want a more QObject::connect() - like approach:

    template<class T, class OBJ, class FUNC>
    void compareNotify(T& value, const T newValue, const T threshold, OBJ *object, FUNC func) {
        if (std::abs(value - newValue) > threshold) {
            value = newValue;
            (object->*func)(value);
        }
    }
    struct Foo {
        void compareAndSet(double val) {
            compareNotify(m_value, int(val), 1, this, &Foo::intValueChanged);
            compareNotify(m_dblValue, double(val), 0.5, this, &Foo::doubleValueChanged);
        }
        void intValueChanged(int v) { std::cout << "int changed to " << v << std::endl; }
        void doubleValueChanged(double v) { std::cout << "double changed to " << v << std::endl; }
        int m_value = 0;
        double m_dblValue = 0;
    };
    int main(int argc, char* argv[]) {
        Foo f;
        f.compareAndSet(2);
        f.compareAndSet(2.6);
    }
    

    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
    Visit the Qt Academy at https://academy.qt.io/catalog

    1 Reply Last reply
    3
    • Christian EhrlicherC Christian Ehrlicher

      @JonB said in template function with signal as parameter:

      telling you to call with a lambda

      Yes, since my first post but I'm getting ignored...

      J.HilkJ Offline
      J.HilkJ Offline
      J.Hilk
      Moderators
      wrote on last edited by
      #11

      @Christian-Ehrlicher Thats sadly normal, theses days.

      As soon as you have more than 1 point of information in your answer, people tend to either focus solely on the first point or the last


      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.

      mzimmersM 1 Reply Last reply
      0
      • J.HilkJ J.Hilk

        @Christian-Ehrlicher Thats sadly normal, theses days.

        As soon as you have more than 1 point of information in your answer, people tend to either focus solely on the first point or the last

        mzimmersM Offline
        mzimmersM Offline
        mzimmers
        wrote on last edited by
        #12

        @J-Hilk @Christian-Ehrlicher I wasn't ignoring anyone's post. I simply don't know lambdas well to enough to fully grasp @Christian-Ehrlicher 's suggestion.

        In looking at @JonB 's annotation, I think the confusion might arise from the misconception that there are multiple instances of the Metrics class, each with its own signal defined. This is not the case; a single object handles all (currently 4) metrics I'm updating.

        Later today, when I'm more fully awake, I'll look more closely to see about implementing the original suggestion. I want to understand it and make it work.

        Thank you for the help.

        mzimmersM 1 Reply Last reply
        0
        • mzimmersM mzimmers

          @J-Hilk @Christian-Ehrlicher I wasn't ignoring anyone's post. I simply don't know lambdas well to enough to fully grasp @Christian-Ehrlicher 's suggestion.

          In looking at @JonB 's annotation, I think the confusion might arise from the misconception that there are multiple instances of the Metrics class, each with its own signal defined. This is not the case; a single object handles all (currently 4) metrics I'm updating.

          Later today, when I'm more fully awake, I'll look more closely to see about implementing the original suggestion. I want to understand it and make it work.

          Thank you for the help.

          mzimmersM Offline
          mzimmersM Offline
          mzimmers
          wrote on last edited by mzimmers
          #13

          So, the problem evidently is with the template declaration:

          // this works.
          void Metrics::compareNotify(double &value,
                                      double newValue,
                                      double threshold,
                                      const std::function<void(double)> &signalFunc ) {...}
          
          // this produces an error:
          // Candidate template ignored: could not match 'std::function<void (T)>' against '(lambda at 
          // C:/Users/Michael.Zimmers/Qt_projects/nga_demo/metrics.cpp:69:19)'
          template <class T> void Metrics::compareNotify(T &value,
                                                         T newValue,
                                                         T threshold,
                                                         const std::function<void(T)> &signalFunc ) {...}
          
          compareNotify(m_flowRate,
                        newFlowRate,
                        FLOW_RATE_NOTIFY_THRESHOLD,
                        [this](double val) { emit this->flowRateChanged(val); });
          

          I'm out of my depth here. Is the problem with the function parameter?

          Thanks...

          Christian EhrlicherC 1 Reply Last reply
          0
          • mzimmersM mzimmers

            So, the problem evidently is with the template declaration:

            // this works.
            void Metrics::compareNotify(double &value,
                                        double newValue,
                                        double threshold,
                                        const std::function<void(double)> &signalFunc ) {...}
            
            // this produces an error:
            // Candidate template ignored: could not match 'std::function<void (T)>' against '(lambda at 
            // C:/Users/Michael.Zimmers/Qt_projects/nga_demo/metrics.cpp:69:19)'
            template <class T> void Metrics::compareNotify(T &value,
                                                           T newValue,
                                                           T threshold,
                                                           const std::function<void(T)> &signalFunc ) {...}
            
            compareNotify(m_flowRate,
                          newFlowRate,
                          FLOW_RATE_NOTIFY_THRESHOLD,
                          [this](double val) { emit this->flowRateChanged(val); });
            

            I'm out of my depth here. Is the problem with the function parameter?

            Thanks...

            Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #14

            @mzimmers said in template function with signal as parameter:

            using callFunc = std::function<void(double)>;
            ...
            template <class T> void Metrics::compareNotify(T &value,
                                                           T newValue,
                                                           T threshold,
                                                           const callFunc  &signalFunc )
            ...
            
            callFunc f = [this](double val) { emit this->flowRateChanged(val);
            compareNotify(m_flowRate,
                          newFlowRate,
                          FLOW_RATE_NOTIFY_THRESHOLD,
                          f);
            
            

            A lambda and a std::function<> don't automatically convert to each other
            Or use the other approach.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            JonBJ mzimmersM 2 Replies Last reply
            1
            • Christian EhrlicherC Christian Ehrlicher

              @mzimmers said in template function with signal as parameter:

              using callFunc = std::function<void(double)>;
              ...
              template <class T> void Metrics::compareNotify(T &value,
                                                             T newValue,
                                                             T threshold,
                                                             const callFunc  &signalFunc )
              ...
              
              callFunc f = [this](double val) { emit this->flowRateChanged(val);
              compareNotify(m_flowRate,
                            newFlowRate,
                            FLOW_RATE_NOTIFY_THRESHOLD,
                            f);
              
              

              A lambda and a std::function<> don't automatically convert to each other
              Or use the other approach.

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

              @Christian-Ehrlicher
              I am interested in the following. Technically by passing a lambda as the function it is up to the caller to pass something which uses the same value as the first parameter. Let's say we wish to impose that value will only be passed as first parameter and a signalfunc will be passed as just a function reference and will be called via signalfunc(value) by compareNotify()'s body code.

              Now, you said earlier this a problem when signalfunc is a class member function. When you do Qt's connect() the syntax is:

              connect(..., slotObject, &SlotClass::method);
              

              How would you write compareNotify() to follow this pattern?

              EDIT Ah, sorry, I see you have already written this above at https://forum.qt.io/topic/143059/template-function-with-signal-as-parameter/10, I did not see, thanks that's great. I have not looked, is connect() indeed written as a template?

              1 Reply Last reply
              0
              • Christian EhrlicherC Christian Ehrlicher

                @mzimmers said in template function with signal as parameter:

                using callFunc = std::function<void(double)>;
                ...
                template <class T> void Metrics::compareNotify(T &value,
                                                               T newValue,
                                                               T threshold,
                                                               const callFunc  &signalFunc )
                ...
                
                callFunc f = [this](double val) { emit this->flowRateChanged(val);
                compareNotify(m_flowRate,
                              newFlowRate,
                              FLOW_RATE_NOTIFY_THRESHOLD,
                              f);
                
                

                A lambda and a std::function<> don't automatically convert to each other
                Or use the other approach.

                mzimmersM Offline
                mzimmersM Offline
                mzimmers
                wrote on last edited by
                #16

                @Christian-Ehrlicher thanks for the more detailed explanation.

                A lambda and a std::function<> don't automatically convert to each other

                Ah, OK. I'm curious -- is there any reason you couldn't use a cast for this conversion (other than it would be hugely ugly)?

                Also, the goal of this exercise was to create a universal routine that would accept a variety of argument types (int, double, etc). While we've technically accomplished this, we've sort of postponed the problem with this:

                using stdFn = std::function<void(double)>;
                

                Is there a way to "templatize" the using statement as well?

                Christian EhrlicherC 1 Reply Last reply
                0
                • mzimmersM mzimmers

                  @Christian-Ehrlicher thanks for the more detailed explanation.

                  A lambda and a std::function<> don't automatically convert to each other

                  Ah, OK. I'm curious -- is there any reason you couldn't use a cast for this conversion (other than it would be hugely ugly)?

                  Also, the goal of this exercise was to create a universal routine that would accept a variety of argument types (int, double, etc). While we've technically accomplished this, we've sort of postponed the problem with this:

                  using stdFn = std::function<void(double)>;
                  

                  Is there a way to "templatize" the using statement as well?

                  Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #17

                  I gave another hint how to use it without std::function but it seems reading is hard nowadays.

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  1 Reply Last reply
                  0
                  • mzimmersM mzimmers has marked this topic as solved on
                  • S Offline
                    S Offline
                    SimonSchroeder
                    wrote on last edited by
                    #18

                    The use of a lambda is actually the easiest (and least error-prone) way to implement this function properly. Modern C++ is now 12 years old and lambdas are really useful. You should really learn that little about modern C++. It is worth it!

                    What you are missing so far is that if you hand over a member function instead of a freestanding function, you have an implicit this parameter. This is why you need to specify the function signature as a member function and you need to provide the object on which the function needs to be called as well.

                    First of all, I personally would advice against std::function in general. It has quite an overhead. Function pointers are a lot more efficient. But, that's just my personal taste. (And there are certain applications where std::function is necessary or the overhead doesn't matter.)

                    The easiest (and not very general) way without a lambda function is to use something like this:

                    template<class T> void Metrics::compareNotify(T &value, T &newValue, T threshold, void (Metrics::*signal)(T))
                    

                    (I'm not entirely sure if I got the syntax right; you might need to google for 'member function pointer')

                    This only allows a signal from the Metrics class itself and assumes that you want to use this within compareNotify as the object for the signal.

                    You can also just let the compiler handle type deduction for you:

                    void Metric::compareNotify(T &value, T &newValue, T threshold, FUNC signal)
                    

                    However, error messages might not be as good (but also might be better!) by using this approach. At least this allows you to easily provide lambda.

                    @Christian-Ehrlicher already posted the most general approach (does not need to be called on this):

                    template<class T, class OBJ, class FUNC>
                    void compareNotify(T& value, const T newValue, const T threshold, OBJ *object, FUNC func)
                    

                    This, however, can also be combined to be a little bit more specific:

                    template<class T, class OBJ>
                    void compareNotify(T&value, T&newValue, T threshold, OBJ *object, void (OBJ::*signal)(T))
                    

                    This restricts that the signal needs to be a member function of the provided object and takes a T as parameter.

                    mzimmersM 1 Reply Last reply
                    4
                    • S SimonSchroeder

                      The use of a lambda is actually the easiest (and least error-prone) way to implement this function properly. Modern C++ is now 12 years old and lambdas are really useful. You should really learn that little about modern C++. It is worth it!

                      What you are missing so far is that if you hand over a member function instead of a freestanding function, you have an implicit this parameter. This is why you need to specify the function signature as a member function and you need to provide the object on which the function needs to be called as well.

                      First of all, I personally would advice against std::function in general. It has quite an overhead. Function pointers are a lot more efficient. But, that's just my personal taste. (And there are certain applications where std::function is necessary or the overhead doesn't matter.)

                      The easiest (and not very general) way without a lambda function is to use something like this:

                      template<class T> void Metrics::compareNotify(T &value, T &newValue, T threshold, void (Metrics::*signal)(T))
                      

                      (I'm not entirely sure if I got the syntax right; you might need to google for 'member function pointer')

                      This only allows a signal from the Metrics class itself and assumes that you want to use this within compareNotify as the object for the signal.

                      You can also just let the compiler handle type deduction for you:

                      void Metric::compareNotify(T &value, T &newValue, T threshold, FUNC signal)
                      

                      However, error messages might not be as good (but also might be better!) by using this approach. At least this allows you to easily provide lambda.

                      @Christian-Ehrlicher already posted the most general approach (does not need to be called on this):

                      template<class T, class OBJ, class FUNC>
                      void compareNotify(T& value, const T newValue, const T threshold, OBJ *object, FUNC func)
                      

                      This, however, can also be combined to be a little bit more specific:

                      template<class T, class OBJ>
                      void compareNotify(T&value, T&newValue, T threshold, OBJ *object, void (OBJ::*signal)(T))
                      

                      This restricts that the signal needs to be a member function of the provided object and takes a T as parameter.

                      mzimmersM Offline
                      mzimmersM Offline
                      mzimmers
                      wrote on last edited by mzimmers
                      #19

                      @SimonSchroeder thank you for the detailed reply.

                      I'm not averse to using lambda functions (I wholeheartedly agree with you on the subject of using "new" language features, even if they raise the complexity bar a bit). What I was really hoping for was a way to eliminate the type-specific code like this:

                      struct Foo {
                          void compareAndSet(double val) {
                              compareNotify(m_value, int(val), 1, this, &Foo::intValueChanged);
                              compareNotify(m_dblValue, double(val), 0.5, this, &Foo::doubleValueChanged);
                      

                      So that my compareNotify could be truly type-agnostic. Perhaps what I'm looking for just isn't feasible, which is fine. The solution that @Christian-Ehrlicher is more than adequate; I was just exploring whether there was another way to do this.

                      I am not a computer scientist by education, and most of what I know is self-taught. C always made innate sense to me, and the early principles of C++ were logical enough. Some of the more modern stuff, though, is a little unintuitive to me. I'm just an old dog trying to learn a few new tricks.

                      EDIT:

                      another note: given that there are only a handful of types in the language, what I'm looking for might be overkill, and might be sacrificing code clarity for universality -- almost never a good trade.

                      1 Reply Last reply
                      1

                      • Login

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