Mit dem Hammer passt C++ durch C



  • 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.



  • Ich fahre nicht unbedingt darauf ab, Makros zu verwenden, aber wenn Makros das einzige Mittel sind, eigentlich unnötige Wiederholungen zu vermeiden, dann verwende ich sie durchaus lokal. Definieren -> Ausstampfen -> Definition löschen sozusagen. Dadurch, finde ich, lassen sich die "höheren Muster", die in etwas stecken, besser überblicken. Ist wahrscheinlich Geschmackssache, weil das ja nun auch auf jeden Fall mit Nachteilen erkauft wird, zugegeben. Auf jeden Fall greife ich zu diesem Mittel erst, wenn es keine schönere Möglichkeit gibt.



  • Meist nur fuer denjenigen, der das Makro schreibt. Ansonsten: Was ist noch schlecht am wrapper-Template, wo wuerdest du hier Makros ansetzen?



  • EinerVonUns schrieb:

    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.

    Falsch.

    zuerst die interne API definieren, danach ist die C API drüber zu legen trivial. Ehrlich. Wenn du gutes C++ hast, legt sich die C API wie Samt darüber.

    Deshalb: schmeiss das weg und mache es in C++ sauber. Dann komm wieder her und zeig uns die saubere C++ API und wir zeigen dir die saubere C API die du drüber legen kannst.



  • Du kannst auch bei bestehenden Bibliotheken nachschauen.

    SFML ist zum Beispiel in sehr sauberem C++ geschrieben. CSFML ist das offizielle C-Binding, welches das C++-API wrappt.



  • Dass der Quellcode, denn ihr gesehen habt, auf der Plugin-Seite für Kompatibilität zu der C-API sorgen soll, das ist aber klar geworden, oder? Ich frag nur mal, weil ich ja schon sagte, dass die C-API auf gewöhnlichem Wege auf der "Host"-Seite um den bestehenden C++-Code entsteht. Also alles worüber ich hier sprechen wollte, ist es, evtl. bei der C-API ein Muster einzuhalten, sodass ein in C++ geschriebenes Plugin ein möglichst leichtes Spiel mit der C-API hat.

    (C)SFML kommt auch auf die Liste, danke! Schaue ich mir alles in meiner Stöberstunde heute Abend an!



  • EinerVonUns schrieb:

    Dass der Quellcode, denn ihr gesehen habt, auf der Plugin-Seite für Kompatibilität zu der C-API sorgen soll, das ist aber klar geworden, oder?

    Nein, ich dachte das sei dein interner Code.

    Warum unterscheidest du C und C++ hier so komisch, und wozu die Templates?

    Du solltest zuerst ein simples C++ Interface bauen, zB

    #include <iostream>
    #include <functional>
    #include <vector>
    
    class Test {
    public:
      typedef std::function<void(Test*)> callback_t;
    
    private:
      std::vector<callback_t> callbacks;
    
    public:
      void registerCallback(std::function<void(Test*)> func) {
        callbacks.push_back(func);
      }
    
      void callCallbacks() {
        for(auto f : callbacks) {
          f(this);
        }
      }
    };
    
    void func(Test* p) {
      std::cout<<"func\n";
    }
    
    int main_cpp() {
      Test t;
      t.registerCallback(func);
      t.callCallbacks();
      return 0;
    }
    
    //und hier das C interface:
    
    typedef void callback_t(void*);
    class Wrapper {
    private:
      callack_t func;
    public:
      Wrapper(callback_t func) : func(func) {}
      void operator()(Test* p) {
        func(static_cast<void*>(p));
      }
    };
    
    void* test_create() {
      return new Test();
    }
    
    void test_destroy(void* o) {
      Test* p=static_cast<Test*>(o);
      delete p;
    }
    
    void test_registerCallback(void* o, callback_t func) {
      Test* p=static_cast<Test*>(o);
      p->registerCallback(Wrapper(func));
    }
    
    void test_callCallbacks(void* o) {
      Test* p=static_cast<Test*>(o);
      p->callCallbacks();
    }
    
    void func2(void* p) {
      std::cout<<"func2\n";
    }
    
    int main_c() {
      void* o=test_create();
      test_registerCallback(o, func2);
      test_callCallbacks(o);
      test_destroy(o);
      return 0;
    }
    
    int main() { return main_c(); }
    

    Edit:
    test_destroy vergessen 😮 (deshalb raii ftw)
    oder übersehe ich irgendwas relevantes?


Anmelden zum Antworten