Übergabe einer Funktion



  • Hallo zusammen,

    Ich habe folgendes Problem: Eine Klasse1 erbt von einer Basisklasse. Die Basisklasse (auf die ich keinen Zugriff habe) enthält eine setCallback-Funktion, der ich eine Methode aus Klasse1 übergeben möchte:

    #include <functional>
    
    using namespace std;
    
    class BasisKlasse {
    public:
    	void setCallback(function<void(char*, uint8_t*, unsigned)> callback) {};
    };
    
    class Klasse1 : BasisKlasse {
    public:
    	void callback(char* p1, uint8_t p2, unsigned p3) {}
    	
    	void setup() {
    		setCallback(&callback);
    	}
    };
    

    Der Aufruf setCallback(&callback) erzeugt natürlich einen Fehler, weil während des Kompilierens noch keine Referenz der Methode callback vorhanden ist.

    Frage an euch: Wie muß man das richtig machen?

    Gruß Joerg



  • @joerg55 sagte in Übergabe einer Funktion:

    Der Aufruf setCallback(&callback) erzeugt natürlich einen Fehler, weil während des Kompilierens noch keine Referenz der Methode callback vorhanden ist.

    Nein, das Problem ist, dass dein callback eine Memberfunktion ist, d.h. sie hat noch "this" als versteckten Parameter.

    Siehe https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr - muss dein callback wirklich eine Memberfunktion sein? Dort ist die Antwort auf "Wie mache ich das" ganz einfach "Don’t."

    Du könntest natürlich sowas machen:

        void setup() {
            setCallback([this](char* p1, uint8_t* p2, unsigned p3){
                this->callback(p1, p2, p3);
            });
        }
    

    Edit: dein uint8_t p2 passt außerdem nicht zu dem uint8_t *-Parameter, aber das ist sicher nur ein Abschreibfehler...



  • @wob

    Au man, da war doch was... 😀

    Manchmal ist Don't wahrscheinlich wirklich der beste Weg. Doch dieser Fall ist (glaube ich) doch ein bißchen anders gelagert als im Link beschrieben.

    Wie auch immer, dein Lösungsvorschlag funktioniert einwandfrei. Besten Dank dafür.

    Ich habe den Trick mit der Lambdafunktion nicht wirklich verstanden. Es wäre super, wenn du noch ein paar erklärende Worte schicken könntest.

    Joerg



  • Lambda expressions ab Lambda capture lesen.



  • Du musst ja eine Funktion mit den 3 Argumenten haben, die unabhängig von dem Objekt ist. Immer wenn du callback aufrufst, brauchst du dazu eine konkrete Instanz von Klasse1, denn du kannst ja nicht Klasse1::callback aufrufen, sondern du musst Klasse1 k1; k1.callback(); aufrufen. Du brauchst also das Objekt k1.

    Das Lambda fängt nun den this-Pointer ein und merkt sich diesen.

    Nimm mal an, dass dein Objekt in einem std::vector liegt. Dann rufst du das setCallback auf. Danach pusht du weitere Elemente in den vector. Danach feuerst du das Callback... Ob das gut geht? Kreuze an:





  • @wob

    dank dir wob für deine Unterstützung. Mein k1 liegt als globale Variable in einem ESP32. Dem geht es also gut 😀 Aber auch sonst würde ich 'ja' ankreuzen. Weil einmal instanziert bleibt k1 doch wohl unabhängig von der 'Speicherung' in einem Vektor an der gleichen Stelle im Speicher. Bei der Zerstörung von k1 wird auch der Aufrufer von callback zerstört. Der sitzt ja in der Basisklasse. Oder verstehe ich jetzt gar nichts mehr?



  • @wob sagte in Übergabe einer Funktion:

    Siehe https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr - muss dein callback wirklich eine Memberfunktion sein? Dort ist die Antwort auf "Wie mache ich das" ganz einfach "Don’t."

    Da geht's aber auch um eine andere Frage.
    Er hat ja hier sowieso schon ne std::function, wieso sollte er dann nicht einen Funktor da reinstecken der eine Memberfunktion aufruft?



  • @hustbaer sagte in Übergabe einer Funktion:

    Da geht's aber auch um eine andere Frage.

    Hm... da war ich wohl zu schnell und ungenau bei der Suche. Habe das "X" in der Überschrift überlesen und nach dem "Don't" mehr oder weniger aufgehört genauer zu lesen 🙂



  • @joerg55 sagte in Übergabe einer Funktion:

    Weil einmal instanziert bleibt k1 doch wohl unabhängig von der 'Speicherung' in einem Vektor an der gleichen Stelle im Speicher.

    Nein. Wenn der Vector wächst wird umkopiert.



  • @manni66

    im Vektor stehen aber nur die Zeiger auf die Objekte und nicht die Objekte selber. Die bleiben doch, wo sie sind...



  • @joerg55 sagte in Übergabe einer Funktion:

    @manni66

    im Vektor stehen aber nur die Zeiger auf die Objekte und nicht die Objekte selber. Die bleiben doch, wo sie sind...

    Nein. std::vector<Klasse1> enthält die Objekte, nicht Zeiger darauf.



  • ...aber wenn dein Objekt, @joerg55, global ist und du auch sowieso nur eine Instanz hast, sehe ich jetzt kein Problem.

    Ich habe den vector erwähnt, weil man dann höllisch aufpasse muss, dass der Code auch so funktioniert wie gewünscht.


Anmelden zum Antworten