Memberfunktion einer Klasse als Callback bei WinApi nutzen?



  • Doch das geht immer noch, sofern nur auch in C bekannte Datentypen als Parameter und Rückgabewert verwendet werden (also keine Klassen).
    Sonst wäre ja auch die Eingangsfrage bzgl. (nicht-statischer) Memberfunktionen als Callback sinnlos.



  • @john-0 sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    ch erinnere mich dunkel daran, dass es einmal möglich war member functions als extern "C" zu deklarieren.

    das hängt damit zusammen, dass c++ funktionen überladen kann. somit kann der funktionsname allein nicht als eindeutige id für den linker herhalten, wie das in c der fall ist.

    aber was passiert hier?

    // in code.cpp
    void f (int a) {...}
    void f (int a, int b) {...}
    
    // in code.c
    extern "C" void f (int);
    extern "C" void f (int, int);   
    

    compile time fehler, already declared oder so?
    müsste eigentlich.



  • @Th69 sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    Doch das geht immer noch, sofern nur auch in C bekannte Datentypen als Parameter und Rückgabewert verwendet werden (also keine Klassen).
    Sonst wäre ja auch die Eingangsfrage bzgl. (nicht-statischer) Memberfunktionen als Callback sinnlos.

    Es wird ja ein Wrapper genutzt. Bei mir compiliert das folgende

    class Test1 {
    public:
        extern "C" void foo(int a);
    };
    
    
    class Test {
    public:
        extern "C" static void foo(int a) noexcept;
    };
    

    jedenfalls nicht, und es gibt diese Fehlermeldung (gcc 7.4.0)

    externc.cc:3:9: error: expected unqualified-id before string constant
      extern "C" void foo(int a);
             ^~~
    externc.cc:9:9: error: expected unqualified-id before string constant
      extern "C" static void foo(int a) noexcept;
             ^~~
    


  • OK, dann reden wir wohl von verschiedenen Sachen.
    Möchtest du in C eine statische Memberfunktion (aus einer C++ Unit) heraus aufrufen?

    Ich meinte: aus C++ heraus, einer in C definierten Funktion eine statische Memberfunktion zu übergeben, à la Ideone-Code (und die Funktion bar käme dann aus einer C-Unit).



  • @Th69 sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    OK, dann reden wir wohl von verschiedenen Sachen.

    Nein, tun wir nicht.

    Ich schreibe davon aus einem C++ Programm ein C Framework zu nutzen, was für Callbacks C Funktionen erwartet. C++ Funktionen erfüllen diese Anforderung nur, wenn sie

    • keine Exceptions werfen, es ist ub es trotzdem zu tun,
    • vom Name Mangling C Funktionen entsprechen,
    • und die sonst auch den Aufrufkonventionen von C Funktionen entsprechen.

    Mit extern "C" ist das auch aus C++ Code heraus garantiert. Es ist keine Frage, dass man normale C++ Funktionen extern "C" deklarieren kann. Interessant wird es, wenn man member functions dafür nutzen will.

    Früher hat man das so gemacht, das lief auf diversen UNICES, weil static member functions den C Funktionen im Aufruf entsprechen. Das ist aber nicht von der Norm garantiert:

    #include <pthread.h>
    #include <cstdlib>
    
    class Thread {
    protected:
        pthread_t threadptr_;
        void Run() {}
    public:
        ~Thread() {
             pthread_join(threadptr_, nullptr);
        }
        static void* thread_func (void* p) {
            Thread* t = reinterpret_cast<Thread*>(p);
            if (t) t->Run();
    
            return nullptr;
        }
        void Create () {
            pthread_create (&threadptr_, nullptr, thread_func, this);
        }
    };
    
    int main () {
        Thread t;
        t.Create();
    
        return EXIT_SUCCESS;
    }
    

    Eine Norm konforme Variante ist:

    #include <pthread.h>
    #include <cstdlib>
    
    extern "C" void* thread_func(void*);
    
    class Thread {
    protected:
        pthread_t threadptr_;
        void Run() {}
    public:
        ~Thread() {
            pthread_join(threadptr_, nullptr);
        }
        void Create() {
            pthread_create (&threadptr_, nullptr, thread_func, this);
        }
        friend void *thread_func (void*);
    };
    
    extern "C" void* thread_func (void* p) {
        Thread* t = reinterpret_cast<Thread*>(p);
        if (t) t->Run();
    
        return nullptr;
    }
    
    int main () {
        Thread t;
        t.Create();
    
        return EXIT_SUCCESS;
    }
    

    Ich bin mir nicht mehr sicher, ob es nicht einmal die Möglichkeit gab member functions (und nur diese) als extern "C" zu deklarieren. Du hast die komplette Klasse als extern "C" deklariert, was etwas anders ist.

    Der nachfolgende Code lässt sich nicht übersetzen.

    #include <pthread.h>
    #include <cstdlib>
    
    class Thread {
    protected:
        pthread_t threadptr_;
        virtual void Run() {}
    public:
        extern "C" static void* thread_func (void* p) {
            Thread* t = reinterpret_cast<Thread*>(p);
            if (t) t->Run();
    
            return nullptr;
        }
        void Create () {
            pthread_create (&threadptr_, nullptr, thread_func, this);
        }
    };
    
    int main () {
        Thread t;
        t.Create();
    
        return EXIT_SUCCESS;
    }
    
    


  • Das extern "C" in meinem Code habe ich nur der einen Klasse hinzugefügt, um zu zeigen, daß dies keinen Effekt diesbgzl. hat.
    Dein erster Code ist normkonform (außer es werden unterschiedliche Aufrufkonventionen [z.B. cdecl vs. stdcall] verwendet).
    Das Name-Mangling spielt doch überhaupt keine Rolle dabei, da du ja von C++ aus die C-Funktion aufrufst: es wird ja nur die Adresse der Funktion an die C-Funktion übergeben (daher war und bin ich weiterhin verwirrt, ob du dies alles so richtig verstehst).



  • @Th69 sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    Dein erster Code ist normkonform (außer es werden unterschiedliche Aufrufkonventionen [z.B. cdecl vs. stdcall] verwendet).

    Das ist nicht der Fall. Der Code funktioniert auf UNIX/Linux (sonst nicht getestet), aber es ist eben nicht von der Norm garantiert, dass er funktioniert. Man sollte nicht den Fehler begehen, das zu einem normkonformen Verhalten umzudeuten.

    Das Name-Mangling spielt doch überhaupt keine Rolle dabei, da du ja von C++ aus die C-Funktion aufrufst: es wird ja nur die Adresse der Funktion an die C-Funktion übergeben (daher war und bin ich weiterhin verwirrt, ob du dies alles so richtig verstehst).

    Das Namemangling spielt da trotzdem eine Rolle, weil die Funktion sich ja in einer shared Library befinden könnte! Das lässt sich dann nicht mehr auflösen, während die Variante mit extern "C"? das tut.



  • extern "C" macht doch bei einer statischen Member-Funktion überhaupt keinen Sinn, da dann ja auch noch der Klassenname mit in den Namen mit einfließen müßte (C kennt ja nur Bezeichner mit Buchstaben, Ziffern und Unterstrich).
    Oder wie soll deiner Meinung nach der Unmangled-Name dafür aussehen?

    Und bzgl. der shared Library verstehe ich deinen Einwand auch nicht - du meinst also doch von C aus, diese C++ Memberfunktion aufzurufen (weil sonst in C++ bindest du einfach die zugehörige Headerdatei ein und rufst diese statische Memberfunktion auf).



  • @Th69 sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    extern "C" macht doch bei einer statischen Member-Funktion überhaupt keinen Sinn

    Special rules for "C" linkage

    1) When class member declarations and member function type declarations appear in a "C" language block, their linkage remains "C++".

    Klingt auch für mich sehr eindeutig.



  • @Finnegan sagte in Memberfunktion einer Klasse als Callback bei WinApi nutzen?:

    1. When class member declarations and member function type declarations appear in a "C" language block, their linkage remains "C++".

    [dcl.link]/4


Log in to reply