C++-Funktion von C aus aufrufen



  • Am Ende deine Klassen-Deklaration fehlt ein Semikolon.



  • Das war beim Übertragen verloren gegangen, im Original ist es vorhanden.


  • Mod

    Warum hat dein C++-Code 'extern C' davor? Das gehört eigentlich nur m den Wrapper Ich weiß gar nicht ob das überhaupt möglich ist, in extern-C-Bereichen C++-typische Sprachmittel wie namespaces oder Klassen zu benutzen.



  • Du kannst im C-Code logischerweise keine C++-Klasse definieren, da Klassen in C nicht existieren. Du kannst aber im Wrapper einen typedef void* wrapped_foo anlegen und den dann zurückgeben. Ansonsten gilt das von SeppJ gesagte, das extern "C" gehört nur um die Deklaration und die Definition des Wrappers. Falls Du einen gemeinsamen Header für Klasse und Wrapper hast, musst Du mit #ifdef __cplusplus die Klasse für den C-Compiler "ausblenden".



  • Irgendwie hab ich es immer noch nicht verstanden... meine Wrapper-Klasse müsste also ungefähr so aussehen?

    #include "foo.h"
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    typedef void* wrapped_foo;
    
                wrapped_foo CreateSomeFooObj()
                {
                    wrapped_foo = /*???*/; //Mir ist noch nicht klar wie ich dann heir meinen Pointer auf die C++-Klasse hineinbekomme
                    return wrapped_foo ;
                };
    
    #ifdef __cplusplus
    }
    #endif
    

    Das Typedef muss doch nach meinem Verständnis innerhalb des "extern c" stehen, damit der C-Compiler es auch kennt. Damit habe ich doch aber wieder keine Möglichkeit auf meine C++-Funktionen zuzugreifen, da ich meine C++-Klassen dann im C-Code bekannt machen muss. Oder habe ich das was gründlich missverstanden?


  • Mod

    Mal ein Beispiel mit dem GCC, aber im Prinzip bei allen Compilern gleich:

    cpp.h:

    #include <string>
    
    namespace foo
    {
      class bar
      {
      public:
        void operator()(const std::string&) const;
      };
    }
    

    cpp.cpp:

    #include <iostream>
    
    #include "cpp.h"
    
    namespace foo
    {
      void bar::operator()(const std::string &arg) const
      {
        std::cout<<"Ein überladener, konstanter Operator in einer Klasse"
          " in einem Namespace. Ziemlich cplusplussig...\n"
          "Ach ja, der übergebene Parameter ist: "
                 << arg << std::endl;
      }
    }
    

    Compilieren mit: g++ cpp.cpp -c -o cpp.o (oder meinetwegen auch als Bibliothek, aber ich will das Beispiel nicht unnötig verkomplizieren)
    **
    wrapper.h**:

    #ifdef __cplusplus
    extern "C"
    {  // Dies sorgt dafür, dass der Header sowohl in C als auch in C++ funktioniert
    #endif
    
      void c_bar(const char *);
    
    #ifdef __cplusplus
    }
    #endif
    

    wrapper.cpp:

    #include <iostream>
    
    #include "wrapper.h"
    #include "cpp.h"
    
    extern "C"
    {
      void c_bar(const char* arg)
      {
        std::cout<<"Der Wrapper ist aktiv:"<<std::endl;
        foo::bar b;
        b(std::string(arg)); 
      }
    };
    

    Compilieren mit: g++ wrapper.cpp -c -o wrapper.o
    Wichtig ist vor allem die Benutzung des C++-Compilers (der hier aber trotzdem C-kompatiblen Code erzeugt).

    c.c:

    #include "stdio.h"
    
    #include "wrapper.h"
    
    int main()
    {
      printf("Jetzt wird von C aus eine C++-Funktion aufgerufen:\n");
      c_bar("Hallo Welt!");
      return 0;
    }
    

    Compilieren mit: gcc c.c -c -o c.o
    Hier darf nun endlich der C-Compiler ran.

    Und zum Schluss noch alles zusammenlinken. Das geht genausogut mit einer Bibliothek für den C++-Code, aber hier im Beispiel als normale Objektdatei:
    g++ cpp.o wrapper.o c.o -o hallo_welt
    Hier wurde der C++ Teil des gcc zum Linken benutzt. Man kann prinzipiell auch den C-Compiler benutzen, in diesem Fall muss man noch die C++-Runtime als Bibliothek dazulinken (dies macht der g++ schon automatisch, ansonsten gibt es keine Unterschiede zwischen den Linkerfrontends).

    Ausführen:

    $> ./hallo_welt
    Jetzt wird von C aus eine C++-Funktion aufgerufen:
    Der Wrapper ist aktiv:
    Ein überladener, konstanter Operator in einer Klasse in einem Namespace. Ziemlich cplusplussig...
    Ach ja, der übergebene Parameter ist: Hallo Welt!
    


  • Randnotiz: Zu beachten ist, dass wenn main() vom C-Compiler übersetzt wird, keine statische Initialisierung und Destruktion stattfindet. Sprich, statische (non-POD-)Memberobjekte oder globale (non-POD-)Objekte im C++-Code sind tabu. Wenn Du das nicht sicherstellen kannst, z.B. weil die C++-Bibliothek nicht von Dir stammt, solltest Du main() unbedingt mit einem C++-Compiler übersetzen.

    Gleiches gilt übrigens für die C++-Standardbibliothek (z.B. cin, cout oder mögliche statische Objekte aller std-Klassen)


  • Mod

    LordJaxom schrieb:

    Randnotiz: Zu beachten ist, dass wenn main() vom C-Compiler übersetzt wird, keine statische Initialisierung und Destruktion stattfindet. Sprich, statische (non-POD-)Memberobjekte oder globale (non-POD-)Objekte im C++-Code sind tabu. Wenn Du das nicht sicherstellen kannst, z.B. weil die C++-Bibliothek nicht von Dir stammt, solltest Du main() unbedingt mit einem C++-Compiler übersetzen.

    Gleiches gilt übrigens für die C++-Standardbibliothek (z.B. cin, cout oder mögliche statische Objekte aller std-Klassen)

    Hmm, stimmt eigentlich. Komischerweise hat es bei meinem Beispiel geklappt, main mit dem C-Compiler zu übersetzen, obwohl cout reichlich verwendet wird. Spezielles Feature des GCC? Undefiniertes Verhalten?



  • Hm, der gcc scheint die statische Initialisierung durchzuführen, sobald ein C++-Objekt im Spiel ist (unabhängig davon, ob g++ oder gcc als Linkerfrontend genutzt werden). Das C++-Objekt hat eine Referenz auf __gxx_personality_v0, welches offenbar von der Bibliotheks-Init-Funktion aufgerufen wird, sobald es vorhanden ist. Dieses scheint die statische Initialisierung durchzuführen. In einem reinen C-Programm, wo __gxx_personality_v0 nicht vorhanden ist, verläuft dieselbe Init-Funktion anders.

    Ich denke aber nicht, dass man sich darauf verlassen kann, dass das alle Compiler so intelligent handhaben.



  • Jetzt habe ich es endlich verstanden, besten Dank nochmal für eure Hilfe!


Anmelden zum Antworten