Callback Funktionen und deren Aufrufe



  • Hallo Zusammen,

    Ich habe eine Frage im Bezug auf Callback Funktionen:

    Ich habe 2 Klassen wovon eine, unter bestimmten Bedingung, die FUnktion der
    zweiten Klassen aufruft(callback). Dazu habe ich auch eine main.cpp zur Erzeugung
    aller Instanzen.

    // Erste Klasse, nachdem eine bestimmte Bedingung bei dem Aufruf
    // von listen_port dieser Klasse eingetreten ist, wird die callback executiert.
    
    void Worker::listen_port(unsigned short upper_bound, void* nm, void (NonsenseManager::*meth_ptr)(void* ptr2obj, unsigned int index) ){
    
        std::srand(time(NULL)) ;
        unsigned short i = upper_bound ;
        unsigned int number ;
    
            for( ; i >=0 ; i--)
                if(i > 1) 
                    while ( (number = std::rand() %i) == 0 )
                        ( ((NonsenseManager *)nm)->*meth_ptr)((void *)nm, i) ; // callback
                 else ( ((NonsenseManager *)nm)->*meth_ptr)((void *)nm, i) ; // callback ;
    }
    //........
    
    // Zweite Klasse deren Funktion als "callback" aufgerufen wird.
    void NonsenseManager::func_quad(unsigned int index){
    
        values_container[index] = index * index ;
    }       
    
    // statische callback function, forwards query to another method         
    void NonsenseManager::wrapper_func(void* ptr2obj, unsigned int index){
    
        NonsenseManager* active = (NonsenseManager *) ptr2obj ;
        cout << __FUNCTION__ << ":callback has been called for index:" << index << endl;
        active->func_quad(index) ;
    }
    

    Bis jetzt klappt alles gut, wenn ich aber durch einen main.cpp die
    Worker->listen_port() aufrufen moechte:

    // von main.cpp
    int main(void)
    {
    
        unsigned short upper = 20 ;
        NonsenseManager* nm = new NonsenseManager(upper) ;
        cout << __FUNCTION__ << ": nonsense manager created" << endl;
        unsigned int i =0 ;
    
        Worker* workoholic = new Worker() ;
        workoholic->listen_port(upper, (void *) nm, &NonsenseManager::wrapper_func); // hier meckert der compiler
        cout << __FUNCTION__ << ": hold tight, workoholic on air" << endl;
    
    return 0;
    }
    

    kriege ich einen Fehlermeldung wie:

    main.cpp:18: error: no matching function for call to ‘Worker::listen_port(short unsigned int&, void*, void ()(void, unsigned int))’
    Worker.h:19: note: candidates are: void Worker::listen_port(short unsigned int, void*, void (NonsenseManager::)(void, unsigned int))

    Wieso klappt der Aufruf von

    workoholic->listen_port(upper, (void *) nm, &NonsenseManager::wrapper_func);
    

    nicht? Ich sehe den Fehler nicht, weil, sowie es bei den Beispielen gezeigt wird,
    muesste so klappen..(Ich habe bei meiner ersten callback implementation im Netz
    vorhandenen Beispiele gefolgt. )
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-126801-and-highlight-is-callback.html
    http://www.newty.de/fpt/callback.html#chapter3

    Danke im voraus,



  • Wie sieht den der Header aus? Also die Deklarationen?

    Im übrigen würde ich dir empfehlen das über Templates zu lösen. Dann kannst du das mit dem void* casten auch gerade weglassen.

    btw.
    Da fehlen auch noch deletes. 😉



  • Hallo drakon,

    drakon schrieb:

    Wie sieht den der Header aus? Also die Deklarationen?

    die header dateien sehen so aus:

    // von Worker.h
    
    class Worker
    {
    
        public:
            Worker() ;
            virtual ~Worker() ;
            void listen_port(unsigned short upper_bound, void* nm, void (NonsenseManager::*meth_ptr)(void* ptr2obj, unsigned int index)); 
    
    };
    
    // von NonsenseManager.h
    
    class NonsenseManager
    {   
        public:
            NonsenseManager(unsigned short no) ;
            virtual ~NonsenseManager() ;
            static void wrapper_func(void* ptr2obj, unsigned int index) ; //callback signatur
            void func_quad(unsigned int index) ;
            void dumpNonsense();
    
        private: 
            unsigned int* values_container ;
            unsigned short upper_bound ;
    
    };
    

    Im übrigen würde ich dir empfehlen das über Templates zu lösen. Dann kannst du das mit dem void* casten auch gerade weglassen.

    ok, danke fuer die Empfehlung. Es geht gerade fuer mich darum, dass ich
    mal nachvollziehe wie callback Funktionen bei c++ implementiert werden.
    Deswegen dieser Weg...

    btw.
    Da fehlen auch noch deletes. 😉

    😃 haste recht, bin gerade dabei...

    Gruss,



  • Der Fehler lag daran, dass eine statische member function keine this pointer
    annehmen darf. Solche statische Funktionen sind direkt addressierbar. Da der
    callback-funktion so deklariert ist:

    static void wrapper_func(void* ptr2obj, unsigned int index) ; //callback signatur
    

    ist die richtige version von listen_port:

    void Worker::listen_port(unsigned short upper_bound, void* nm, void (*meth_ptr)(void* ptr2obj, unsigned int index) ){
    
        std::srand(time(NULL)) ;
        unsigned short i = upper_bound ;
        unsigned int number ;
    
            for( ; i >=0 ; i--)
                if(i > 1) 
                    while ( (number = std::rand() %i) == 0 )
                        (*meth_ptr)((void *)nm, i) ; // callback
                 else (*meth_ptr)((void *)nm, i) ; // callback ;
    }
    

    Gruss,



  • Ich würde das mit Interface-Klassen lösen. Je nach komplexität bzw. gewünschter Flexibilität, wären Templates hier vielleicht etwas übertrieben.

    Irgendwie so z.B.:

    #include <iostream>
    #include <vector>
    
    //Interface
    class ICallbackInterface
    {
    public:
        virtual int someCallback(int param1, int param2) = 0;
    };
    
    //callback handler: verwaltet callbacks und führt sie aus
    class CCallbackHandler
    {
    public:
        //registriert callbacks
        void addCallback(ICallbackInterface& callback)
        {
            myCallbacks.push_back(&callback);
        }
    
        //ruft alle registrierten callbacks auf
        void executeCallbacks()
        {
            for(std::vector<ICallbackInterface*>::iterator iter = myCallbacks.begin(); iter != myCallbacks.end(); ++iter)
            {
                std::cout << (*iter)->someCallback(1, 3) << '\n';
            }
        }
    
    private:
        //speichert die registrierten callbacks
        std::vector<ICallbackInterface*> myCallbacks;
    };
    
    //bindet das interface ein und kann daher beim handler registriert werden
    class CCallbackUser1 : public ICallbackInterface
    {
    public:
        virtual int someCallback(int param1, int param2)
        {
            return param1 + param2;
        }
    };
    
    //bindet das interface ein und kann daher beim handler registriert werden
    class CCallbackUser2 : public ICallbackInterface
    {
    public:
        virtual int someCallback(int param1, int param2)
        {
            return param1 * param2;
        }
    };
    
    int main( )
    {
        //callbacks
        CCallbackUser1 cbu1;
        CCallbackUser2 cbu2;
    
        //handler
        CCallbackHandler handler;
    
        //callbacks registrieren...
        handler.addCallback(cbu1);
        handler.addCallback(cbu2);
    
        //...und ausfuehren
        handler.executeCallbacks();
        return 0;
    }
    


  • jsbach schrieb:

    ...

    Wobei sich natürlich die Frage stellt, warum das static, bzw. IN der Klasse ist. Da könntest du natürlich die Funktion aus der Klasse nehmen, da sie ja nix davon nix wissen muss/kann/darf.

    Tachyon schrieb:

    Ich würde das mit Interface-Klassen lösen. Je nach komplexität bzw. gewünschter Flexibilität, wären Templates hier vielleicht etwas übertrieben.

    In seinem Fall wäre das wahrscheinlich eleganter. Jedoch wollte er Methoden Pointer verstehen. Da hilft es ihm nix, wenn er dann ein Interface draus macht. 😉
    Wenn ich Templates in Verbindug mit Methoden Pointer einsetze, dann wissen die meis überhaupt nix über die andere Klasse.


Anmelden zum Antworten