Wie caste ich von std::function nach LPVOID und zurück



  • @firefly

    Also so ne richtig schöne Erklärung habe ich nirgends gefunden.
    Aber vielleicht gibt der Artikel hier Aufschluss.

    https://www.c-plusplus.net/forum/topic/294038/lebenszeit-von-lambda-funktionen/7



  • Du übergibst aber per LPVOID(&func) die Adresse der lokalen Variablen func als Callback-Parameter (nicht einen [globalen] Funktionszeiger) - in dem anderen Beitrag wird ja jeweils beim v.push_back(f)eine Kopie des std::function<...>-Parameters erzeugt.

    Daher noch mal meine Frage:

    Kann es sein, daß der Callback erst später aufgerufen wird, also wenn InstallDataService schon wieder verlassen wurde (siehst du ja am Stacktrace bzw. wenn du einen Breakpoint nach dem Aufruf setzt)?



  • @Th69

    Da haben wirs 🙂

    Aber anders kann ich das ja gar nicht übergeben. Weil es ja ein Zeiger sein muss. Danke dass muss es sein.
    Also muss ich die Funktion doch irgendwie als Klassenmember anlegen.



  • @booster sagte in Wie caste ich von std::function nach LPVOID und zurück:

    Ja natürlich du hast recht. Die Funktion hatte ich inline definiert:

    auto func = [=](shared_ptr<wincc::DmDataService> spds)
    

    Wie schwer ist es zu verstehen, dass C Callbacks unbedingt auch als extern C definiert sein müssen? Wenn Du Glück hast, hat die member function wincc::DmDataService gerade zufällig die passende C Aufrufkonvention, falls Du sie static definiert hast. Aber das ist nirgends in der ISO Norm garantiert. Man übergibt auch keinen shared_ptr auch was auch immer sondern immer einen rohen C Zeiger!



  • @john-0 sagte in Wie caste ich von std::function nach LPVOID und zurück:

    Wie schwer ist es zu verstehen, dass C Callbacks unbedingt auch als extern C

    Hallo John

    Wo muss das als extern C definiert sein?
    hier?

    static BOOL OnReceiveData(LPDM_DATA_SERVICE lpds, LPVOID lpvUser);
    

    und wieso darf ich keinen shared_ptr übergeben?
    Ich erzeuge mir den Speicher nachher in der callback. Wenn ich nen rohen zeiger verwende und den speicher mit new anlege. Muss ich mich um das löschen kümmern. Und wann mache ich dass. Nachdem der Empfänger die Nachricht verarbeitet hat. Das ist dann nicht schön wenn new und delete in unterschiedlichen Funktionen sind.



  • @john-0: Es geht hier um einen Callback-Parameter, nicht um den Callback-Aufruf selbst (die Funktion InstallDataService wird sicherlich als extern "C"deklariert sein).



  • @Th69
    Ach das meint er. Danke.

    @Th69 sagte in Wie caste ich von std::function nach LPVOID und zurück:

    die Funktion InstallDataService wird sicherlich als "extern "C"`deklariert sein

    Sollte sie.

    Aber endet nach ein paaar typedefs hier:

    BOOL WINAPI DMInstallDataServiceA(
        LPCSTR lpszService,
        DM_DATA_SERVICE_PROCA lpfnService,
        LPVOID lpvUser,
        LPCMN_ERRORA lpdmError);
    

    Aber darauf habe ich keinen Einfluss.



  • Der ganze Header wird dann per

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    // ...
    
    #ifdef __cplusplus
    }
    #endif
    

    umschlossen sein.



  • @Th69

    Wie recht du hast. Hatte ich ganz übersehen.
    War etwas irritiert was John da wollte. Aber passt.

    Jetzt weiß ich nur noch nicht warum ich keinen shared_ptr verwenden sollte?

    Das andere hat im übrigen fukntioniert. Klassenmember aus der Funktion gemacht und schon gehts. Nun muss ich halt noch schauen wie ich das ganze verwalte wenn ich mehrere functions "installieren" möchte.



  • @booster sagte in Wie caste ich von std::function nach LPVOID und zurück:

    Wo muss das als extern C definiert sein?
    hier?

    Weil C Funktionen und C++ Funktionen eine andere Calling Convention haben, z.B. member functions übergeben immer verdeckt den this parameter, das Exception Handling von C++ spielt da auch eine Rolle. Es wird nur für extern C Funktionen garantiert, dass sie exakt den Calling Conventions von C entsprechen. D.h. macht man eine member function static kann das funktionieren, aber es gibt keine Garantie -> UB. Möglicherweise steht etwas dazu in der Doku. Es gibt keinen Weg eine Member Function extern C zu definieren, also bleibt nur der Weg einer eigenen Funktion. Benutzte deshalb eine simple extern C Wrapper Function, die alle Exceptions abfängt, und dann Deine Member Function aufruft.

    Ich kenne die Win API nicht, aber OnReceiveData sieht mir nicht nach einer C++ API aus, so dass wahrscheinlich hier ebenfalls C Konventionen notwendig sein werden. Es sieht mir auch nicht nach einer C++ Wrapper Funktion aus an die man eine C++ Funktion übergeben könnte. Das würde eher eine abgeleitete Klasse oder für den Wrapper ein Template erfordern OnReceiveData hat weder das eine noch das andere im Interface.

    und wieso darf ich keinen shared_ptr übergeben?

    Pass auf, dass da kein Durcheinander entsteht. Normale Zeiger lassen sich mit einem simplen static_cast konvertieren. Dazu bietet sich der this-Pointer an. In der C Wrapper Funktion castest Du den void* Zeiger auf den Klassentypen Deiner Klasse und rufst die eigene Member Function auf. Die shared_ptr etc. legst Du ganz normal als Member in der Klasse an, da brauchst Du nichts von Hand zu machen.

    Nachtrag:
    Als Beispiel wie das so aussehen sollte ein kleines rudimentäres Beispiel mit pthreads und passender Callback Funktion. In diesem Beispiel ließe sich das leicht erweitern, in dem man einfach von der Klasse thread ableitet und dann kann man pro Ableitung eine neue Member Function run definieren.

    #include <pthread.h>
    #include <cstdlib>
    #include <iostream>
    #include <boost/core/noncopyable.hpp>
    
    class thread_attr : private boost::noncopyable {
        pthread_attr_t  attr;
    public:
        thread_attr () {
            pthread_attr_init (&attr);
        }
        ~thread_attr() {
            pthread_attr_destroy (&attr);
        }
        pthread_attr_t* get() {
            return &attr;
        }
    };
    
    extern "C" {
        void* callback (void* arg) noexcept;
    }
    
    class thread : private boost::noncopyable {
        thread_attr attr;
        pthread_t   thr;
    public:
        virtual void run() {
            std::cout << "thread::run" << std::endl;
        }
        thread () {
            pthread_create(&thr,attr.get(),&callback,this);
        }
        ~thread() {
            pthread_join(thr,nullptr);
        }
    };
    
    extern "C" {
        void* callback (void* arg) noexcept {
            thread* tr = static_cast<thread*>(arg);
    
            try {
                tr->run();
            }
            catch (...) {
                std::cerr << "callback exception" << std::endl;
            }
    
            return nullptr;
        }
    }
    
    
    int main () {
        thread          t1;
    
        return EXIT_SUCCESS;
    }
    
    

Anmelden zum Antworten