Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

generalize a c++ method



  • hi,
    I'm developing a OPC UA Client application.

    I wrote c++ code that allows me to subscribe to a variable that is in the server, then every time the variable will change in the server, my application will recive the new value.

    this is the subscription code :

    QScopedPointer<QOpcUaNode>m_axesPosNode;
    
    m_axesPosNode.reset(m_client->node("ns=1;s=theName"));
    
    m_axesPosNode->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(50));
    
    QObject::connect(m_axesPosNode.data(), &QOpcUaNode::attributeUpdated, this, &MachineBackend::axesPosUpdated);  // axesPosUpdated will be called every tile value changes on the server
    
    

    I want to write a method that does the same

    void uaSub(QScopedPointer<QOpcUaNode> &node, QString nodeId, void (*theUpdateHandler)(QOpcUa::NodeAttribute, const QVariant &)) {
    
        node.reset(m_client->node(nodeId));
        node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(200));
        QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,[=](QOpcUa::NodeAttribute attr,const QVariant &value){
            theUpdateHandler(attr,value);
        });
    
    }
    // use it
     uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*nIndexUpdated);
    
    

    My problem is *theUpdateHandler must be static (so i can pass it to another function), and because it is static i can not access members of my class in it..

    void static nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){
       {
        Q_UNUSED(attr); 
        qDebug()<< "new value is  " << value;  
        //m_url =  value.toString();   i cant access Q_properties /  signal ...
    }
    

    Can someone tell me how to handle this please ?
    this is more lack of knowledge in c++ than pure qt related question sorry for that

    thank you


  • Lifetime Qt Champion

    @LeLev See https://stackoverflow.com/questions/14315346/function-pointer-of-a-non-static-member-function-of-a-class
    With non-static methods you need an instance of the class to call the method:

    typedef void (T::*MethodPtr) ();
    MethodPtr method = &T::MethodA;
    T *obj = new T();
    obj->*method();
    

  • Lifetime Qt Champion

    Hi
    Just to be sure i understand.
    You think it needs to be static to be used as a parameter
    OR
    you required it to be a plain c function pointer for the OPC UA to be able to use it later ?
    (since you have a

    You can use a class member as a parameter
    https://www.quantstart.com/articles/Function-Objects-Functors-in-C-Part-1
    https://isocpp.org/wiki/faq/pointers-to-members

    However, the instance having the function should stay alive then and not sure if you want that.



  • hi ! thanks for the answer
    @mrjj said in generalize a c++ method:

    You think it needs to be static to be used as a parameter

    yes, I thought that to be able to pass a function A to another function B, A must be static.

    thanks for the links i will update the solution here if i can



  • Not sure what the links above cover, but std::bind can bind the instance of the object as the first parameter while leaving other parameters open in the resulting functor.



  • hi,
    @fcarney thx , i'm trying to understand how to use std::bind here (i have never used it)
    @mrjj the articles are very interesting but i am not able to find the solution that fits my need..

    please let me show you better my situation, so you can maybe suggest one solution

    class UaClient : public QObject
    {
        Q_OBJECT
    
    public:
    // CREATES A SUBSCRIPTION, TAKES A METHOD (theUpdateHandler)  THAT WILL BE CALLED LATER
    void uaSub(QScopedPointer<QOpcUaNode> &node, QString nodeId, void (*theUpdateHandler)(QOpcUa::NodeAttribute, const QVariant &)) {
    
        node.reset(m_client->node(nodeId));
        node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(200));
        QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,[=](QOpcUa::NodeAttribute attr,const QVariant &value){
            theUpdateHandler(attr,value);
        });
    
    }
    
    private :
    // UPDATE HANDLER , HAS TO ACCESS MEMBER VARIABLES, HAS TO BE PASSED TO uaSub()
     static void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){  // m_someMember = value.toString(); 
             return;
         }
    
    QString m_someMember;
    };
    
    ...
    // use the function to create subscriptions and pass the handler functions for every subscription
    uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*nIndexUpdated); 
    uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*speedValueUpdated); 
    uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*xxxUpdated); 
    
    

    this will work but handler methods (ex : nIndexUpdated ) can't access member variables because they are static.

    If i don't mark it static then I have an error in the uaSub method

    error  : reference to non-static member function must be called
    invalid use of memeber 'void UaClient::nIndexUpdated(QOpcUa::NodeAttribute,const QVariant&)' (did you forget the '&'  ? )
    

    So i still can't figure out how to pass a method to another method (when they both declared in the same class, and they are not static)


  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    when they both declared in the same class

    If they are both in the same class then why do you want to mess around with pointers? I would rather use an enum to specify what I want to execute. You only have 3 different methods in your example, so using pointers will not give you any performance advantages.



  • @mrjj do you mean like this ? this looks good except i will have a huge switch

      enum class UpdateHandlers: qint32{
            NINDEX = 0, 
            SPEED = 1,  
            XXX = 2
        };
    
    void MachineBackend::uaSub1(QScopedPointer<QOpcUaNode> &node, QString nodeId, UpdateHandlers theHandler){
        
        node.reset(m_client->node(nodeId));
        node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(200));
        QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,[=](QOpcUa::NodeAttribute attr,const QVariant &value){
                  
            switch (theHandler) {
            
            case UpdateHandlers::NINDEX :   nIndexUpdated(attr,value);break;
            case UpdateHandlers::SPEED  :   nSpeeedUpdated(attr,value);break;
            case UpdateHandlers::XXX    :   xxxUpdated(attr,value);break;
            default: break;            
            }              
        });  
        
        return;    
    }
    
    
    
    


  • edit @mrjj sorry not for you

    @jsulm
    do you mean this ?

     enum  UpdateHandlers : qint32{
            NINDEX, 
            SPEED, 
            XXX
        };
    
    
    void MachineBackend::uaSub1(QScopedPointer<QOpcUaNode> &node, QString nodeId, UpdateHandlers theHandler){
        
        node.reset(m_client->node(nodeId));
        node->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(200));
        QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,[=](QOpcUa::NodeAttribute attr,const QVariant &value){
           
            
            switch (theHandler) {
            
            case UpdateHandlers::NINDEX :   nIndexUpdated(attr,value);break;
            case UpdateHandlers::SPEED  :   nSpeeedUpdated(attr,value);break;
            case UpdateHandlers::XXX    :   xxxUpdated(attr,value);break;
            default: break;
                
            }
            
            
        });
        
        
        return;
        
    }
    

  • Lifetime Qt Champion

    @LeLev See https://stackoverflow.com/questions/14315346/function-pointer-of-a-non-static-member-function-of-a-class
    With non-static methods you need an instance of the class to call the method:

    typedef void (T::*MethodPtr) ();
    MethodPtr method = &T::MethodA;
    T *obj = new T();
    obj->*method();
    


  • @jsulm i think i can not use this last solution, because there is only one instance of this class. it is instantiated in the main.cpp and putted as contextProperty, it is never destroyed and has same life time as my application. I can not create another object of this class


  • Moderators

    @LeLev said in generalize a c++ method:

    @jsulm i think i can not use this last solution, because there is only one instance of this class. it is instantiated in the main.cpp and putted as contextProperty, it is never destroyed and has same life time as my application. I can not create another object of this class

    ok, when this - in essence - is a singleton, why don't you make the member variable then also static?



  • because i'm JS guy ... i 'm learning Qt/cpp only for 2 years now.
    let me try it



  • @J-Hilk said in generalize a c++ method:

    why don't you make the member variable then also static?

    i have one member variable per subscription,
    each member variable has a Q_PROPERTY corresponding to it, so i can use the variable value from QML.

    is it possible do define Q_PROPERTIES for a static member variable ?

    Im trying but getting errors :
    moc_machinebackend.cpp:-1: erreur : undefined reference to `MachineBackend::m_url'

     Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
    
    public:
     QString url()const{
           return MachineBackend::m_url; 
        }
        void setUrl(const QString nUrl){
            if(MachineBackend::m_url==nUrl)return;
            MachineBackend::m_url = nUrl;
            emit urlChanged(nUrl);
        }
    
    private : 
    static QString m_url;
    
    signals:
    void urlChanged(QString url);
    

  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    i think i can not use this last solution

    Sure you can: you pass the pointer to second method when you call the first method, right? And since both methods are not static you call the first method already on an instance (which is "this" inside the method):

    this->*method(); // method is the pointer to the other method
    


  • @jsulm said in generalize a c++ method:

    : you pass the pointer to second method when you call the first method, right?

    Yes, but it only works if the
    2nd method is static !

    //first 
    void uaSub(QScopedPointer<QOpcUaNode> &node, QString nodeId , void (*theUpdateHandler)(QOpcUa::NodeAttribute attr,const QVariant &value)){...}
    // 2nd
    static void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){...}
    
    
    uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*nIndexUpdated){...}
    
    //Here **nIndexUpdated** must be static !  no ?
    

    if it is not static i get :
    reference to non-static member function must be called error


  • Lifetime Qt Champion

    @LeLev Please take a closer look at the snippet I provided before:

    typedef void (T::*MethodPtr) ();
    MethodPtr method = &T::MethodA;
    T *obj = new T();
    obj->*method();
    

    Your syntax is not even correct as you have to use & to get a pointer not *
    So in your case something like

    class A
    {
        typedef void (A::*MethodPtr) ();
        void nIndexUpdated() {}
        void uaSub(..., MethodPtr ptr) {
            this->*ptr();
        }
    
        // Somewhere inside class A you call uaSub
        uaSub(/*params*/, &A::nIndexUpdated);
    }
    


  • @jsulm
    thank you so much for the help you provide.
    Unfortunately i'm so bad, i can't make this work

    class MachineBackend : public QObject
    {
        typedef void (MachineBackend::*MethodPtr) ();
        Q_OBJECT
    
    void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){...}
    
    void uaSub2(QScopedPointer<QOpcUaNode>&node, QString nodeId, MethodPtr updateHandlerPtr){
           
           this->*updateHandlerPtr(); // < error here 
    
        }
    };
    

    called object type 'MachineBackend ::MethodPtr' (aka 'void (MachineBackend ::*)()' ) is not a function or function pointer


  • Lifetime Qt Champion

    @LeLev Try

    this->updateHandlerPtr();
    


  • @jsulm said in generalize a c++ method:

    this->updateHandlerPtr();

    yes i did also, in that case error is :

    no member named 'updateHandlerPtr' in 'MachineBackend '
    which is true


  • Lifetime Qt Champion

    @LeLev This should compile:

    (this->*updateHandlerPtr)();
    

    Method pointers are a mess :-)



  • yes now it doas. awesome ! thank you so much!

    class MachineBackend : public QObject
    {
        typedef void (MachineBackend::*MethodPtr) ();
        Q_OBJECT
    
    void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){...}
    
    void uaSub2(QScopedPointer<QOpcUaNode>&node, QString nodeId, MethodPtr updateHandlerPtr){
           
           (this->*updateHandlerPtr)();
    
        }
    };
    
    
    
    

    there is still an issue in the usSub() call

    //  Somewhere inside class
      uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex", MachineBackend::nIndexUpdated);  // << error
    //
    

    error : reference to non-static member function must be called

    im trying every possible syntax right now ...


  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex", MachineBackend::nIndexUpdated);

    uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex", &MachineBackend::nIndexUpdated);
    

  • Lifetime Qt Champion

    @LeLev This small example works for me:

    #include <iostream>
    
    class A
    {
    public:
        typedef void (A::*MethodPtr) ();
        void someMethod() { std::cout << "Hallo!" << std::endl; }
        void uaSub2(MethodPtr updateHandlerPtr){
           (this->* updateHandlerPtr)();
        }
    };
    
    int main()
    {
        A a;
        a.uaSub2(&A::someMethod); 
        return 0;
    }
    


  • @jsulm yes sorry that is the first one i have tryed:

    uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex", &MachineBackend::nIndexUpdated);
    
    // err :  cannot initialize a parameter of type 'Machinebackend::MethorPtr' (aka 'void (MAchinebackend::*)()') with an rvalue of type 'void (MAchineBAckend::*)(QOpcUa::NodeAttribute, const QVariant &)' : different number of parameters (0 vs 2)
    
    error : no matching function for call to 'MachineBackend::uaSub2(QScopedPointer<QOpcUaNode>&, const char [19], <unresolved overloaded function type>)'
             uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex",MachineBackend::nIndexUpdated);
                                                                                 ^
    

  • Lifetime Qt Champion

    This post is deleted!

  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    nIndexUpdated

    This one has parameters, you need to adapt your typedef for the pointer accordingly.
    This is what the error tells you.



  • @jsulm

    class MachineBackend : public QObject
    {
        typedef void (MachineBackend::*MethodPtr) ();
        Q_OBJECT
    
    void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){...}
    
    void uaSub2(QScopedPointer<QOpcUaNode>&node, QString nodeId, MethodPtr updateHandlerPtr){
           
           (this->*updateHandlerPtr)();
    
        }
    };
    

    in the constructor of this class i call m_client.connectToServer(), then connect stateChanged signal of the m_client to a method where i want to create the subscriptions :

     QObject::connect(m_client.data(), &QOpcUaClient::stateChanged, this, &MachineBackend::clientStateHandler);
    
    void MachineBackend::clientStateHandler(QOpcUaClient::ClientState state)
    {
        setConnected((state == QOpcUaClient::ClientState::Connected)); 
    
        if (state == QOpcUaClient::ClientState::Connected) {// if client is connected i create subscriptions
                     
            // uaSub(nIndexNode,"ns=4;s=MAIN.nIndex",*nIndexUpdated); // function pointer to static function
    
            // uaSub1(nIndexNode,"ns=4;s=MAIN.nIndex", MachineBackend::NINDEX) // enum
            
             uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex",&MachineBackend::nIndexUpdated);   
    
        }
    
       [...]
    }
    
    

    I think i have to adapt my typedef because &QOpcUaClient::stateChanged returns 2 parameters


  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    I think i have to adapt my typedef

    Yes, you have to specify the parameter types when defining a function/method pointer type.



  • i'm having troubles to adapt the typedef..
    should it be

    typedef void (MachineBackend::*MethodPtr) (QOpcUa::NodeAttribute attr,const QVariant &value);
    
     or 
    
    typedef void (MachineBackend::*MethodPtr(QOpcUa::NodeAttribute attr,const QVariant &value)) ();
    

  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    typedef void (MachineBackend::*MethodPtr) (QOpcUa::NodeAttribute attr,const QVariant &value);

    typedef void (MachineBackend::*MethodPtr) (QOpcUa::NodeAttribute, const QVariant&);
    

    No need for parameter names, only types.



  • @jsulm

    class MachineBackend : public QObject
    {
        typedef void (MachineBackend::*MethodPtr) (QOpcUa::NodeAttribute, const QVariant&);
    
        Q_OBJECT
    
    public:
        void uaSub2(QScopedPointer<QOpcUaNode>&node, QString nodeId, MethodPtr updateHandlerPtr){
     
          //  trying to use lambda to call my pointed function but can't
    
          /*  QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,[&]{
                 (this->*updateHandlerPtr() ) ();
            });
         */ 
      
             // maybe  is it possible to connect to my function directly  ?     
         QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,this,&MachineBackend:: ??)
    
        }
        
    
    };
    

  • Lifetime Qt Champion

    @LeLev said in generalize a c++ method:

    (this->*updateHandlerPtr() ) ();

    (this->*updateHandlerPtr) (); <--- You have to pass 2 parameters to it!
    


  • @jsulm yes but i don't have any parameter to pass to it .. .

    its just a handler function that must be connected to
    QOpcUaNode::attributeUpdated witch returns 2 values ;

    that is why it has to have 2 params.

    I don't want to call that function, i want it to be connected to

    QOpcUaNode::attributeUpdated , and called automatically when attributeUpdated is emitted in the opcua backend



  • @jsulm finally it is this working thanks to you !

    class MachineBackend : public QObject
    {
        typedef void (MachineBackend::*MethodPtr) (QOpcUa::NodeAttribute, const QVariant&);
    
        Q_OBJECT
    
    
    public:
    
    
        void uaSub2(QScopedPointer<QOpcUaNode>&node, QString nodeId, MethodPtr updateHandlerPtr){
    
         QObject::connect(node.data(),&QOpcUaNode::attributeUpdated,this,updateHandlerPtr);
    
        }
    
      void nIndexUpdated(QOpcUa::NodeAttribute attr, const QVariant &value){
            //return; // i can access member variables / signal / methods here
        }
    
    
    // use uaSub
    
    uaSub2(nIndexNode,"ns=4;s=MAIN.nIndex",&MachineBackend::nIndexUpdated);
    //
    
    };
    

    thank you infinitely everybody who helped me


Log in to reply