Mit dem Hammer passt C++ durch C



  • Achso, du willst die C++ Funktion auch in C über Wrapper aufrufen können? Dir ist aber schon klar, dass ein C Compiler keine Templates kennt? Und wenn er das kann, dann ist er ein C++ Compiler und kan mann direkt in C++ programmieren. 😕

    Mir erschließt sich das so gar nicht.



  • Man kann sehr wohl nach außen C Funktionen anbieten und intern in C++ schreiben.



  • Wozu soll das gut sein? C ist quasi irrelevant...



  • Kellerautomat schrieb:

    Wozu soll das gut sein? C ist quasi irrelevant...

    lol.



  • Kellerautomat schrieb:

    Wozu soll das gut sein? C ist quasi irrelevant...

    Bei vielen Programmiersprachen-Implementierungen gibt es eine Möglichkeit C Funktionen aufzurufen und zT ist es sogar möglich C Structs zu nutzen als wären sie eine Instanz eines zu Structs äquivalenten Sprachfeatures (Stichwort: foreign function interface).

    Ein C Interface anzubieten ist somit ein guter Anfang, wenn man seine Bibliothek für viele nutzbar machen möchte.



  • @EinerVonUns: Hast du dir eigentlich mal angeschaut, wie andere Bibliotheken Interfaces sowohl fuer C als auch fuer C++ anbieten? Wohl nicht. Auch ist es mir unklar, warum du es generisch, d.h. mit Templates, loesen musst. Hinzu kommt, dass ich echte Probleme mit deinem Deutsch habe. Ich verstehe die einzelnen Worte, nur nicht den Satz. Beispiel:

    Nun möchte man auf der internen Seite ja Zeiger auf Funktionen halten können, die dann der Kommunikationspartner auf der anderen Seite der C-API bekannt machen kann.

    Und normalerweise kann man eine C-API problemlos in C und C++ verwenden. Was willst du eigentlich uns mitteilen? Dein Post ist sehr nah am Trollen.



  • Du schreibst einfach eine C++ API wie es sich gehört. Wenn du fertig bist, klatscht du ein C Interface drüber. Fertig.

    Aus

    Klasse obj(1,2,3);
    obj.foo();
    

    wir einfach

    void* obj = create_klasse(1,2,3);
    foo(obj);
    

    in C

    PS:
    und noch ein lol @ Kellerautomat



  • Du schreibst einfach eine C++ API wie es sich gehört. Wenn du fertig bist, klatscht du ein C Interface drüber. Fertig.

    Wahlweise auch anders herum.



  • Also wenn ich nah am Trollen liege, dann tut's mir Leid... irgendwie sind dann aber andere hier schon des längeren fern auf der anderen Seite der Trollgrenze, ohne dass es dort von euch auch nur angemahnt würde...

    Mir geht es darum, dass ich keine C++-Compiler-Spezifische API extern anbieten möchte (Es gibt nunmal keine ABI). Die einzige Lösung, die mir dazu einfällt, ist es, dass auch in C++ geschriebene Erweiterungen sich durch die C-API durchquetschen müssen. Die C-API entsteht genau so, wie ihr es hier erwähnt: Sie wird als Wrapper um meinen existierenden C++-Code gelegt, der ohne Hinblick auf irgendwelche C-APIs entstand/entsteht.

    Mir geht es jetzt hauptsächlich darum, wie ich die C-API definiere, sodass eine C++-Erweiterung auf der anderen Seite möglichst wenig Aufwand hat, die C-API abermals zu wrappen. Und was ich an Helfer-Klassen für eine C++-Erweiterung dann anbieten kann. Eine solche Helferklasse wäre das beschriebene template, das für eine Memberfunktion einen C-Einstiegspunkt erstellt und das ganze fertig für die C-API verpackt (wenn auch mit ekliger Syntax, für deren Vereinfachung mir nur Makros einfallen).

    Nun möchte man auf der internen Seite ja Zeiger auf Funktionen halten können, die dann der Kommunikationspartner auf der anderen Seite der C-API bekannt machen kann.

    Das ist folgendermaßen gemeint: Die Host-Applikation soll Funktionszeiger zu Erweiterungs-Funktionen halten (Event-Handler oder ähnliches). Das wird in erster Linie ein C-Funktionszeiger und ein untypisierter Parameter sein. So kann eine C-Erweiterung und eine C++-Erweiterung zwischen Objektinstanzen, die beim Funktionsaufruf gemeint sind, unterscheiden (was wie ich auch erwähnte eine Standardtechnik beispielsweise in der Win32-API ist, ohne letztere jetzt qualitativ zu bewerten).

    Zuletzt habe ich auch im letzten Satz darauf hingewiesen, dass ich durchaus unvoreingenommen an guten Ansätzen anderer interessiert bin. Ihr wisst doch aber selber wahrscheinlich am besten, dass hier auf die Frage "Nennt mir ein Open-Source C++-Projekt" mit schönem und modernem C++ ungefähr 20 Antworten zu erwarten sind, die sich alle gegenseitig widersprechen. Ich würde gerne einige Beispiele durchschauen, aber die Suche nach "Gutes C++ Projekt C/C++-Plugin-API" führt zu nichts. Wenn ihr da etwas empfehlen könnt, dann _BITTE_, lasst mich daran teilhaben.



  • Wo genau ist das Problem ein Callback anzubieten?

    In C++ machst du das per functor und in der C API baust du dir aus dem reinen Funktionszeiger einen functor (uU ist dies sogar implizit Möglich). Du musst lediglich die Calling Convention beachten (die uU auch gleich ist).

    Nochmal:
    Du baust zuerst die interne API fertig, so wie sie sein soll. Und wenn sie fertig ist, dann klatscht du ein C Interface drüber.

    Auf welche konkreten Probleme bist du dabei denn gestoßen? Der C++ Code sah mir zu wirr aus um mich da durchzusehen. Ist diese riesige Komplexität wirklich notwendig? uU kennst du einfach nur std::function nicht?



  • Benutz Interfaces, dann braucht man kein C API -> C ist irrelevant



  • Kellerautomat schrieb:

    Benutz Interfaces, dann braucht man kein C API -> C ist irrelevant

    Du meinst sowas wie ISomething? Nun, bei Ableitung und Mehrfachvererbung gibt es Probleme ueber Librarygrenzen hinweg. Selbst erlebt. Dynamic dispatch virtueller Funktionen wird problematisch.

    Nennt mir ein Open-Source C++-Projekt" mit schönem und modernem C++

    Modernes C++ ist 2 Jahre alt, Projekte sind meist aelter. Projekte mit Callbacks oder C++/C-API: fltk und ZeroMQ.

    PS: Ich verstehe immer noch nicht dein Problem. Weiterhin glaube ich, dass dein Programm UB ist. Ein Methodenzeiger ist grundsaetzlich anders als ein "normaler" Funktionszeiger wie in C.
    http://stackoverflow.com/questions/12006854/why-the-size-of-a-pointer-to-a-function-is-different-from-the-size-of-a-pointer
    http://stackoverflow.com/questions/16062651/about-sizeof-of-a-class-member-function-pointer

    D.h. was du machst ist falsch. Bestenfalls so (Pseudocode):

    typedef struct c_callback_t
    { 
        WrapperFuncPtr func; // mit spezifischer signatur fuer callback
        void* obj;
    } c_callback; 
    
    class my_obj
    {
        int my_func(params ...);
    };
    
    int my_obj_wrapper(void* obj, params ...)
    {
       my_obj* pobj = (my_obj*)(obj);
       return pobj->my_func(params ...)
    }
    
    // using from C++ with call trough C-API
    
    my_obj yes_we_can;
    c_callback_t ywc_wrap;
    
    ywc_wrap.obj = yes_we_can;
    ywc_wrap.func = my_obj_wrapper; // no method pointer just normal function pointer
    

    Okay, ich sehe, dass du das so aehnlich machst, aber ... c_callback_t muss nicht das gleiche Layout wie irgendeine Klasse generiert aus dem Templatewust haben.

    auto* cl = reinterpret_cast<typename MemFunc::Class*>(uarg);
    

    Hier castest du einfach zu einem Memberfunctionpointer. Was wenn es gar kein Memberfunctionpointer ist?



  • Kellerautomat schrieb:

    Benutz Interfaces, dann braucht man kein C API -> C ist irrelevant

    lol.



  • Kellerautomat schrieb:

    Benutz Interfaces, dann braucht man kein C API -> C ist irrelevant

    Kannst du dich hier bitte raushalten wenn du nix intelligentes zu sagen hast?
    Danke.



  • Danke, die beiden Projekte werde ich mir anschauen.

    UB ist da doch eigentlich nichts? Was an die C-API weitergereicht wird, ist doch ein Zeiger auf eine statische Klassenfunktion mit definierter Calling-Convention? Gut, die statische Funktion wird erst durch ein Template erzeugt, aber ist doch an sich zu behandeln wie eine freie Funktion?

    Ich könnte selbstverständlich in der C++-Erweiterung auch eine einzelne "Aufruf-Routine" (oder meinetwegen statische Dispatcherroutine, oder wie man das nun benennen mag) haben, und der void* parameter der C-API würde dann als Zeiger auf ein std::function-Objekt genutzt, oder als Zeiger auf eine Struktur, die die methodenspezifische std::function enthält und einen this-Zeiger). Es führen da ja sicherlich 1000 Wege nach Rom und weil ich schwer absehen kann, was es nun im genauen bedeutet, wenn ich diesen oder jenen Weg einschlage, wende ich mich an euch, die ihr das schoneinmal gemacht habt und vielleicht auch einmal ein paar Fehler, aus denen man lernen könnte. Ich lerne derweilen erstmal aus meinen eigenen 😉



  • @knivil: Na klar, das reinterpret_cast muss natürlich davon ausgehen können, dass der void*-Zeiger durch das inverse reinterpret_cast entstand. Das irgendwie sicherer zu machen ist wohl kaum möglich, zumindest fällt mir da nichts ein, hast Du eine Idee? Da muss die Erweiterung der API trauen und die API muss der Erweiterung trauen, dass sie das schon irgendwie richtig macht.



  • Lol, ich merke was du eigentlich machst. Hier in einfach:

    #include <iostream> 
    using std::cout;
    
    typedef void(*callback_t)(void*);
    
    void call_cb(callback_t func, void* pobj) {
        func(pobj);
    }
    
    struct Test {
        void func() {
            std::cerr << "i am Test" << '\n';
        }
    };
    
    void wrap_test_func(void* pobj) {
        static_cast<Test*>(pobj)->func();
    }
    
    int main() {
        Test a;
        call_cb(wrap_test_func, &a);
    
        return 0;
    }
    

    Du kannsty gerne wrap_test_func in ein Template verwandeln, aber das was du machst wuerde ich aufgrund der Layoutzweifel als falsch empfinden. Und darueber hinaus: Einfach nur Overkill.

    Nachtrag mit wrapper als Templatate:

    template<typename T, typename void (T::*method)(void)>
    void wrapper(void* pobj)
    {
        (static_cast<T*>(pobj)->*method)();
    }
    
    int main()
    {
        Test a;
        call_cb(wrap_test_func, &a);
        call_cb(wrapper<Test, &Test::func>, &a);
    
        return 0;
    }
    


  • Ja, die Idee war, dass ich die Callbacks für die C-API in solcher Form definiere, dass ein template, das eine C-Wrapper-Funktion für eine Methode erzeugt, wenigstens die Signatur auf Kompatibilität checken kann.

    Und jetzt ist doch eine interessante Fragestellung aus diesem Beispiel entstanden: Ist das wegen etwaiger Regeln zu C/C++-Layouts wirklich UB, was ich da mache? Darf man von einer C-Struktur sozusagen ableiten und bedeutet dass, das der Teil des Objektes dem C-Alignment folgt? Ich slice ja den Rest weg (genau genommen ist da ja im Moment gar nichts anderes, den abgeleiteten Typ mal außen vor).
    Mit der Technik könnte ich halt einen Satz von Wrapper-Funktionen per Makro definieren, ohne immer einen kompletten Funktionskörper mit void*-cast schreiben zu müssen.
    Andererseits könnte ich den Körper auch direkt aus einem Makro stampfen. Müsste mal schauen, wie das aussähe.



  • Ich habe das Template nachgeliefert. Trotzdem bedeutet es, dass du zumindestens fuer jede Signatur einen extra Template brauchst.

    Darf man von einer C-Struktur sozusagen ableiten und bedeutet dass, das der Teil des Objektes dem C-Alignment folgt?

    Das weiss ich nicht. Da es fuer C++ aber keine ABI spezifiziert wurde, kann eine Ableitung von c_callback_t durchaus anders aussehen als c_callback_t.

    Mit der Technik könnte ich halt einen Satz von Wrapper-Funktionen per Makro definieren

    Kannst du jetzt auch, ganz ohne Makros. Ich verstehe nicht, warum alle so auf Makros abfahren, obwohl der eigentliche Konsenz ist, das sie boese sind.



  • Wobei man sich das typedef da in der C-API jetzt auch sparen kann, weil man ja decltype zur hand hat. Ich spiele einmal mit den Alternativen herum.

    Das hier ist auf jeden Fall nur als Beispiel gedacht für die Möglichkeiten, die man evtl. hat, wenn man bei der C-API auf ein gewisses Muster achtet.


Anmelden zum Antworten