Adressübergabe einer Memberfunktion



  • Hallo Experten,

    ich möchte mich mit Pointer auf Memberfunktionen vertraut machen. Die Adresse von der Memberfunktion z.B. 'void person::listen' möchte ich in einer <map> inkl. einer ID hinterlegen um später darauf zuzugreifen (es sollen spätere mehrere sein - daher die ID).
    Compiler bringt folgende Fehlermeldung : "invalid use of void expression" für die Codezeile 66.
    Was mache ich falsch ?
    Vielen Dank im voraus !

    Hier mein Code:

    //------- person.h -------
    #ifndef PERSON_H
    #define PERSON_H
    #include <iostream>
    
    class message;
    
    class person
    {
    public:
        Person(std::string name);
        void listen();
    
    private:
        std::string const _name;
        message* _say;
    };
    #endif // PERSON_H
    
    //--------- person.cpp ---------
    #include "person.h"
    
    person::person(std::string name)
    {
     _name = name;
    }
    
    void person:: listen()
    {
    
    }
    
    //--------- message.h --------
    #ifndef MESSAGE_H
    #define MESSAGE_H
    
    #include <functional>
    #include <map>
    #include "person.h"
    
    class message
    {
    public:
        message();
    
        int connect_member(person *inst, void(person::*func)());
    
    private:
      mutable std::map<int, std::function<void>> slots_;
      mutable int current_id_;
    };
    
    #endif // MESSAGE_H
    
    //--------- message.cpp --------
    #include "message.h"
    
    message::message()
    {
    
    }
    
    int message::connect_member(person *inst, void(person::*func)()){
    
        slots_.insert(std::make_pair(++current_id_, (inst->*func)())); //HIER IST DER FEHLER
        return current_id_;
    
        return 0;
    }
    

    Compilerausgabe:

    C:/Qt/Qt5.9.3/Tools/mingw530_32/bin/mingw32-make -f Makefile.Debug
    mingw32-make[1]: Entering directory 'C:/_apd/Qt_workspace/build-signal2-Desktop_Qt_5_9_3_MinGW_32bit-Debug'
    g++ -c -fno-keep-inline-dllexport -g -std=gnu++11 -Wextra -Wall -W -fexceptions -mthreads -DUNICODE -D_UNICODE -DQT_QML_DEBUG -I..\signal2 -I. -I..\..\..\Qt\Qt5.9.3\5.9.3\mingw53_32\mkspecs\win32-g++ -o debug\message.o ..\signal2\message.cpp
    ..\signal2\message.cpp: In member function 'int message::connect_member(person*, void (person::*)())':
    ..\signal2\message.cpp:11:64: error: invalid use of void expression
    slots_.insert(std::make_pair(++current_id_, (inst->*func)()));
    ^



  • (inst->*func)()
    

    führt die Funktion aus. Das Ergebnis ist void.



  • Hallo Manni,
    Danke für deine schnelle Antwort.
    Ich möchte einfach nur die Adresse einer Memberfunktion hinterlegen um diese später wieder referenzieren zu können.
    Soweit ich es verstehe hängt die "void"-Fehlermeldung mit der Codezeile 64 zusammmen, da die Memberfunktion den Rückgabewert void hat.



  • schnebe schrieb:

    Soweit ich es verstehe hängt die "void"-Fehlermeldung mit der Codezeile 64 zusammmen, da die Memberfunktion den Rückgabewert void hat.

    Ja, wie ich bereits sagte, wird die Funktion dort ausgeführt. Das Ergebnis wird eingefügt.

    Du könntest eine Lambdaausdruck verwenden (ungetestet!)

    int message::connect_member(person *inst, void(person::*func)()){
    
        slots_.insert(std::make_pair(++current_id_, [=]() {(inst->*func)();} )); //HIER IST DER FEHLER
        return current_id_;
    }
    

    Aber eigentlich solltest du entweder beide Pointer speichern oder connect_member direkt eine std::function übergeben.



  • Hi,

    ok-das mit der std:function ist sicherlich generischer. Aber das ist ja nicht mein "Problem".
    Mit Lambda-Funktionen habe ich noch keine Erfahrung und sind mir nur grob bekannt. Warum wird über die Lambdafunktion durch deren Funktionrumpf {inst->*func() } dann doch irgendwie die Adresse in der <map> hinterlegt 🙄 ?
    Kannst du mir das genauer erklären?

    Wenn es tut möchte ich es natürlich verstehen. Dann wäre es sicherlich einfacher anstatt die Instanz und den Zeiger auf die Memberfunktion zu verwalten.

    Danke!
    Bernd



  • Lambda zu Fuß (ungetestet!):

    int message::connect_member(person *inst, void(person::*func)()){
        struct Lambda
        {
           person *inst;
           void(person::*func)();
           void operator()() const { (inst->*func)(); }
           Lambda(person *inst, void(person::*func)()) : inst(inst), func(func){}
        };
    
        slots_.insert(std::make_pair(++current_id_, Lambda(inst, func) ));
        return current_id_;
    }
    

    Ansonsten erklärt jedes aktuelle C++ Buch Lambdas.



  • Hallo Manni,
    ich hoffe ein letztes Mal.
    Das mit der Lambda-Funktion ist aktuell für mich noch zu "hoch". Das lassen wir mal.
    Ich will ja nur verstehen wie man die Adressen von Memberfunktionen speichert und wieder darauf zugreifen kann.
    Du schreibst weiter oben "
    "... oder connect_member direkt eine std::function übergeben."

    Ich brauche doch immer das Objekt selbst. Der Zeiger auf dessen Memberfunktion genügt doch nicht. Oder doch ? 🙄

    Ich habe mal die connect_member(...) so wie du es vorgeschlagen hast umgebaut.
    Mir ist unklar wie der Aufruf von connect_member(...) bzw. die Adressübergabe erfolgt.
    Den Code habe ich etwas abgeändert. "Person" entfällt, dafür gibt es einen "Sender" und "Receiver".
    In der Zeile 38 mache ich was falsch!

    //--- message.cpp ---
    int message::connect_member(std::function<void()>& slot){
    
        slots_.insert(std::make_pair(++current_id_, slot));
        return current_id_;
    }
    
    //-- receiver.h ---
    class receiver
    {
    public:
        receiver();
        message _say;
    
        int listen();
    };
    
    //--- sender.h ---
    class sender
    {
    public:
        sender();
        message _say;
    };
    
    //--- main ---
    int main()
    {
        sender bob;
        receiver alice;
    
        typedef int (receiver::*rcvFunc)();
        receiver *pw = &alice;
        rcvFunc memberFunc = &receiver::listen;
        //(pw->*memberFunc)();
    
        int id = bob._say.connect_member(memberFunc); // ???????
    
        return 0;
    }
    


  • schnebe schrieb:

    Das mit der Lambda-Funktion ist aktuell für mich noch zu "hoch". Das lassen wir mal.

    Das ist mit Sciherheit einfacher als die hampelei mit Funktionspointern.

    schnebe schrieb:

    Ich will ja nur verstehen wie man die Adressen von Memberfunktionen speichert und wieder darauf zugreifen kann.
    ...
    Ich brauche doch immer das Objekt selbst. Der Zeiger auf dessen Memberfunktion genügt doch nicht. Oder doch ? 🙄

    Ja, wenn du mit Funktionspointern auf nicht statische Member rumhampelst, brauchst du auch ein Objekt dazu.

    schnebe schrieb:

    Du schreibst weiter oben "
    "... oder connect_member direkt eine std::function übergeben."

    Ja, und dann verwendest du ein Lambda ohne Pointerhampelei (ungetestet!):

    int main()
    {
        sender bob;
        receiver alice;
    
        auto listen = [&](){ alice.listen(); };
    
        int id = bob._say.connect_member( listen ); 
    
    ...
    


  • Leider kommt noch eine Fehlermeldung:

    ..\sender_receiver\main.cpp: In function 'int main()':
    ..\sender_receiver\main.cpp:19:44: error: no matching function for call to 'message::connect_member(main()::<lambda()>&)'
    int id = bob._say.connect_member(listen);
    ^
    In file included from ..\sender_receiver\sender.h:4:0,
    from ..\sender_receiver\main.cpp:2:
    ..\sender_receiver\message.h:11:9: note: candidate: int message::connect_member(std::function<void()>&)
    int connect_member(std::function<void()>& slot);
    ^
    ..\sender_receiver\message.h:11:9: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'std::function<void()>&'
    ..\sender_receiver\main.cpp:19:9: warning: unused variable 'id' [-Wunused-variable]
    int id = bob._say.connect_member(listen);

    Wenns tut dann laß ich dich in Ruhe :p



  • Hier:

    int message::connect_member(std::function<void()>& slot){
    

    Warum hast du hier eine (non-const) Referenz auf eine std::function?



  • Warum ist eine const Ref. auf std:function erforderlch ?

    Wie müsste diese schreiben?

    // message.h
    int connect_member(std::function<void()> const& slot);
    
    // message.cpp
    int message::connect_member(std::function<void()> const& slot)
    {
    slots_.insert(std::make_pair(++current_id_, slot));
        return current_id_;
    }
    


  • Der Rückgabetyp paßt nicht überein: void <-> int



  • ok der Rückgabetyp war falsch - habe nun

    void message::listen() definiert
    

    leider kommt immer noch folgende Fehlermeldung:
    ..\sender_receiver\main.cpp: In function 'int main()':
    ..\sender_receiver\main.cpp:13:44: error: no matching function for call to 'message::connect_member(main()::<lambda()>&)'
    int id = bob._say.connect_member(listen);
    ^
    In file included from ..\sender_receiver\sender.h:4:0,
    from ..\sender_receiver\main.cpp:2:
    ..\sender_receiver\message.h:13:9: note: candidate: int message::connect_member(std::function<void()>&)
    int connect_member(std::function<void()> & slot);
    ^
    DIESE FEHLERMELDUNG DÜRFTE AM AUSSAGEFRÄFTIGSTEN SEIN:
    ..\sender_receiver\message.h:13:9: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'std::function<void()>&'

    //message.cpp
    int message::connect_member(std::function<void()> & slot){
    
        slots_.insert(std::make_pair(++current_id_, slot));
        return current_id_;
    }
    
    // receiver.h
    class receiver
    {
    public:
        receiver();
        message _say;
        void listen();
    };
    
    //main
    int main()
    {
        sender bob;
        receiver alice;
    
        auto listen = [&](){ alice.listen(); };
        int id = bob._say.connect_member(listen);
    
        return id;
    }
    


  • int message::connect_member(std::function<void()>  slot)
    


  • Nun kommt eine Templatefehlermeldung !?!?

    ..\sender_receiver\message.cpp:9:45: error: template argument 1 is invalid
    int message::connect_member(std::function<()> slot){


  • Mod

    Suchspiel: Finde den Unterschied!

    schnebe schrieb:

    int message::connect_member(std::function<()> slot)
    

    manni66 schrieb:

    int message::connect_member(std::function<void()>  slot)
    


  • Danke Manni - tut!

    Ich nehme an daß der &-Operator in der "connect_member" entfällt , da durch die Bindung [&] bereits eine Referenz zurück gegeben wird.



  • schnebe schrieb:

    Ich nehme an daß der &-Operator in der "connect_member" entfällt , da durch die Bindung [&] bereits eine Referenz zurück gegeben wird.

    Solltest du das & an std::function meinen: Nein! Eine std::function kann ein Lambda entgegennehmen, ein Lambda ist aber keine std::function.



  • Ok!
    Bin mittlerweile auch bei der Lambdafunktion schlauer geworden durch gute Beispiele. Ich muß es jetzt "nur" für meinen Anwendungsfall umsetzen.

    Nochmal Danke an alle!


Anmelden zum Antworten