Anzeige:
Ergebnis 1 bis 15 von 15

Thema: extern "C"

  1. #1
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20

    extern "C"

    Hallo,

    gibt es eine Möglichkeit ein C-Programm mit C++-Code zu erweitern?

    Ich habe ein Opensource-Programm, das in C geschrieben ist und möchte in einer einzelnen Datei C++-Code per include einfügen und auch verwenden. Ich bräuchte in dem C-Code also sowas wie ein extern "C++". Das gibt es aber vermutlich nicht... Ich dachte, wenn ich das ganze Programm mit g++ und nicht mit gcc kompiliere, könnte es evtl. klappen. Hab dazu im Makefile gcc durch g++ ersetzt, hat aber leider nix gebracht. Er meckert dann trotzdem an meiner Klasse rum. Muss allerdings sagen, dass das ein, für meine Verhältnisse, sehr großes Makefile ist und ich bin mir nicht ganz sicher ob das ausreichend war was ich im Makefile geändert hab.

    Hat jemand sowas schon mal gemacht oder eine Idee? Hab irgendwo gelesen, dass ich da einen Wrapper schreiben muss. Kann mir allerdings nicht vorstellen wie das funktionieren soll. Aber lasse mich gerne eines besseren belehren

    Grüße
    Sebastian

  2. #2
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Der C Code kann natürlich keine C++ Klassen benutzen, aber du kannst natürlich relativ einfach eine C Wrapper machen, in dem du für jede Methode eine entsprechende C Funktion machst, deren erster Parameter immer die Objektinstanz ist.

    Quasi mehr oder weniger so

    C++ Header
    Code:
    class Foo
    {
    public:
        Foo(int num) : m_num(i) {}
    
        int num() const { return m_num; }
    
    private:
        int m_num;
    };
    C Header
    Code:
    struct Foo;  // Foo muss nicht näher deklariert werden, man benutzt das nur als Pointer
    
    Foo* createFoo(int num);
    
    void destroyFoo(Foo* foo);
    
    int getNumFoo(Foo* foo);
    Im Source wäre das dann etwa so

    Code:
    // C++ Header inkludieren
    
    extern "C" {
    Foo* createFoo(int num)
    {
        return new Foo(num);
    }
    
    void destroyFoo(Foo* foo)
    {
        delete foo;
    }
    
    int getNumFoo(Foo* foo)
    {
        return foo->num();
    }
    
    }
    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  3. #3
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    das sieht doch schon mal interessant aus! Danke

    aber irgendwie ist mir das zu umständlich; habs mir etwas einfacher vorgestellt Vermutlich kann ich dann auch nicht den vollen Sprachumfang von C++ benutzen - ich denke da an so Sachen wie Überladen von Funktionen, Stichwort virtual usw.

    Falls sich jemand für das Thema "C und C++ mischen" interessiert, hab ich noch nen interessanten Link gefunden: http://parashift.com/c++-faq-lite/mixing-c-and-cpp.html

    Von einem Kollegen hab ich den Tipp bekommen das Ganze über Sockets zu machen. Die Erweiterung für das C-Programm also als eigenständiges C++ Programm schreiben und die Kommunikation über Sockets abwickeln. Ein weiterer Vorteil wäre, dass ich die Erweiterung auch auf einem anderen Rechner laufen lassen könnte. Das ist meiner Meinung nach nicht die schönste Lösung aber auch nicht hässlicher als die Wrapper-Klasse

    Nochmals Danke für Deine Hilfe!

    Grüße
    Sebastian

  4. #4
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Das ist eine ganz andere Kategorie. Out-of-process ist natürlich flexibler und unabhängiger, aber auch schwieriger, weil ja parallel gearbeitet wird, Kommunikation und Sychronisation anfällt, etc.

    Eine C API lässt sich quasi automatisationsgestützt generieren, selbst zu C++ das Klassen verwendet (siehe Beispiel).
    Das ist dann nicht viel anders, als objektorientierte Programmierung in C.

    Ein Beispiel für sowas ist TagLib, eine Bibliothek zur Verarbeitung von Metadaten aus Mediendateien. In C++ geschrieben, aber mit zusätzlicher C API.

    Im dem Fall, dass du ein Modell mit kommunizierenden Prozessen vorziehst, rate ich dazu, D-Bus als Kommunikationsmedium zu benutzen.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  5. #5
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    Das schöne an der Erweiterung ist, dass ich nicht wirklich viel synchronisieren muss. Das C-Programm liefert einfach ein paar Daten an das C++-Programm, das daraus wieder neue Daten berechnet und diese an das C-Programm zurückschickt. Hab mir gedacht ich lasse die Kommunikation in einem eigenen Thread laufen, damit nicht das ganze C-Programm schläft während das C++ Programm rechnet. Für so einen einfachen Datenaustausch sollten normale Sockets reichen. Dann weiss ich was passiert und muss mich auch nicht irgendwo neu einarbeiten

    Gruß
    Sebastian

  6. #6
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Dann könnstest du einfacher sogar eine Pipe zur Kommunikation verwenden, bzw. das C++ als Kind ausführen und über stdin/stdout kommunizieren.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  7. #7
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    hab leider noch nie Pipes zur Kommunikation verwendet (ausser in der Kommandozeile ). Werds mir aber mal anschauen.

    Das schöne an den Sockets ist, dass ich das C++ Programm evtl. auch auf einem anderen Rechner laufen lassen kann. Da es sich dabei um einen genetischen Algorithmus handelt mit Laufzeiten im Sekunden- bis Minutenbereich, wäre das evtl. sogar von Vorteil, da man den Algorithmus dann auf einem schnelleren Rechner laufen lassen könnte.

    Danke für Deine Unterstützung! Kommt meiner Dipl-Arbeit bestimmt zu Gute

  8. #8
    Administrator Avatar von anda_skoa
    Registriert seit
    17.11.2001
    Ort
    Graz, Österreich
    Beiträge
    5.477
    Sockets, besonders Netzwerksockets, haben das Problem, dass praktisch jeder verbinden kann.

    Ist natürlich bei einer Diplomarbeit nicht so wichtig, in einer realen Applikation sollte man das aber berücksichtigen.

    Außerdem müssen die Verbindungsparameter irgendwie bekannt gegeben/konfiguriert werden müssen, etc.

    Ciao,
    _
    Qt/KDE Entwickler
    Debian Benutzer

  9. #9
    Registrierter Benutzer Avatar von panzi
    Registriert seit
    04.05.2001
    Ort
    Kottingbrunn
    Beiträge
    609
    Wenn es eh so eine einfache Kommunikation ist (also A fragt B und B Antwortet) dann brauchst eh nicht mehr als 3 Methoden deiner Klasse zu wrappen. Einfach so wie anda_skoa esgezeigt hat: Eine C-Funktion um ein Objekt anzulegen, eine Funktion um es zu löschen und eine die als Parameter die "Frage" bekommt und die "Antwort" als Returnwert. Die C++ Klasse kann dann intern soviele andere Klassen/Methoden/was auch immer verwenden wie sie will. Gewrapped müssen nur die 3 werden.
    Intel Core 2 Duo CPU 2.66GHz; Nvidia GeForce 8 8800 GTS; 4GB RAM; Fedora 12; KDE-testing

  10. #10
    Registrierter Benutzer Avatar von panzi
    Registriert seit
    04.05.2001
    Ort
    Kottingbrunn
    Beiträge
    609
    Zusätzlich kann bei Sockets praktisch zu jedem Zeitpunkt die Verbindung reißen. Das muss behandelt werden damit das Programm mit so einer Situation umgehn kann. Das erfordert aufwendigen Fehlerbehandlungscode, da es kein C++ Sockets mit Exceptions gibt. Man muss 100.000 Returnwerte abfragen.
    Intel Core 2 Duo CPU 2.66GHz; Nvidia GeForce 8 8800 GTS; 4GB RAM; Fedora 12; KDE-testing

  11. #11
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    hab das mit der Wrapper-Geschichte mal ausprobiert und es funktioniert bei mir leider nicht.

    Hab folgendes gemacht:

    - eine einfache C++ Klasse erstellt (also cplusplus.h und cplusplus.cpp ähnlich wie im Beispielcode s.o.)
    - Wrapper erstellt (.h und .cpp)

    wrapper.h
    Code:
    #ifndef _WRAPPER_H_
    #define _WRAPPER_H_
    
    struct Cpp;
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif /* ifdef __cplusplus */
    
    12: Cpp* createCPP(int);
    13: int getNum(Cpp*);
    14: void deleteCpp(Cpp*);
    
    #ifdef __cplusplus
    }
    #endif /* ifdef __cplusplus */
    
    
    #endif
    wrapper.cpp
    Code:
    #include "wrapper.h"
    #include "cplusplus.h"
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif /* ifdef __cplusplus */
    
    Cpp* createCPP(int n) {
    	return new Cpp(n);
    }
    
    int getNum(Cpp* temp) {
    	return temp->num();
    }
    
    void deleteCpp(Cpp* temp) {
    	delete temp;
    }
    
    #ifdef __cplusplus
    }
    #endif /* ifdef __cplusplus */
    - test.c mit main()

    test.c
    Code:
    #include <stdio.h>
    #include "wrapper.h"
    
    
    int main(int argc, char** argv) {
    	Cpp* test;
    
    	test = createCPP(5);
    	printf("Test: %d\n", getNum(test));
    	deleteCpp(test);
    
    	return 0;
    }
    - compiliert

    c++ Klasse mit "g++ -c cplusplus.cpp"
    wrapper mit "g++ -c wrapper.cpp"
    test.c mit "gcc -c test.c"

    cplusplus.cpp und wrapper.cpp werden ohne Fehler compiliert. Bei der test.c gibt es Fehler:

    Code:
    seven@seven:~/dipl/tmp> gcc -Wall -c test.c
    In file included from test.c:3:
    wrapper.h:12: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token
    wrapper.h:13: error: expected ‘)’ before ‘*’ token
    wrapper.h:14: error: expected ‘)’ before ‘*’ token
    test.c: In function ‘main’:
    test.c:7: error: ‘Cpp’ undeclared (first use in this function)
    test.c:7: error: (Each undeclared identifier is reported only once
    test.c:7: error: for each function it appears in.)
    test.c:7: error: ‘test’ undeclared (first use in this function)
    test.c:9: warning: implicit declaration of function ‘createCPP’
    test.c:10: warning: implicit declaration of function ‘getNum’
    test.c:11: warning: implicit declaration of function ‘deleteCpp’
    Es scheint als ob er das Cpp* test; nicht mag. In der wrapper.h ist ein struct Cpp; definiert.
    Der Zeiger test (aus test.c) müsste ja eigentlich auf eine struct Cpp zeigen. Die Funktion createCpp() (aus wrapper.cpp) liefert mir aber doch einen Zeiger auf eine Instanz der Klasse Cpp zurück. Kann das überhaupt funktionieren oder hab ich irgendwas falsch verstanden?

  12. #12
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Es scheint als ob er das Cpp* test; nicht mag. In der wrapper.h ist ein struct Cpp; definiert.
    Wenn du in C ein struct so definierst,
    "struct X { };", dann musst du es mit dem Keyword struct verwenden, also z.B.
    "struct X x1;" um eine Variable zu definieren. In C++ reicht ja bekanntlich "X x1;" aus.
    Du kannst aber in C auch so etwas hier "typedef struct X { } X;" definieren. Dann kannst du auch in C "X x1;" sagen.

    Hier übrigens der Beitrag aus der C++ Faq zum Thema Mixen von C und C++:
    http://parashift.com/c++-faq-lite/mixing-c-and-cpp.html

  13. #13
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    stimmt, C unterscheidet sich da wohl etwas.
    Hab in die test.c ein "struct" vor das "Cpp* test" gesetzt, leider ohne Erfolg.

    Fehlermeldung:

    Code:
    seven@seven:~/dipl/tmp> make clean && make
    rm -f *.o test
    gcc -c  test.c
    In file included from test.c:3:
    wrapper.h:12: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token
    wrapper.h:13: error: expected ‘)’ before ‘*’ token
    wrapper.h:14: error: expected ‘)’ before ‘*’ token
    test.c: In function ‘main’:
    test.c:9: error: ‘Cpp’ undeclared (first use in this function)
    test.c:9: error: (Each undeclared identifier is reported only once
    test.c:9: error: for each function it appears in.)
    test.c:9: error: expected expression before ‘)’ token
    make: *** [test.o] Fehler 1
    Ich werd das wohl etwas anders machen müssen. Brauch im C-Code auch nicht unbedingt nen Pointer auf die Instanz.
    Aber falls jemand noch ne Idee hat - immer her damit ;-)

  14. #14
    Registrierter Benutzer Avatar von jeebee
    Registriert seit
    01.01.2005
    Ort
    Bern || Zürich
    Beiträge
    540
    du musst nur noch das struct auch in wrapper.h:12 vor Cpp* setzen (wohl auch in wrapper.cpp -- damit die Signaturen übereinstimmen.) Schlussendlich ist das ja auch Code den der C-Compiler verstehen muss.

    MfG jeebee
    my very own 128 bit integer
    C4 D3 B8 A8 9E A0 C6 EC 7D EC A8 15 28 D1 92 58
    more information

  15. #15
    Registrierter Benutzer
    Registriert seit
    13.06.2007
    Beiträge
    20
    danke Dir (und allen anderen, die geholfen haben)! Damit gehts. Leider kann ich das Ganze nur mit dem g++ linken und nicht mit dem gcc. Muss ich mich wohl doch noch in autoconf/automake einarbeiten

    [Für Alle, die keinen Pointer wollen, sondern ein handle auf die Instanz, werden hier fündig: http://www.heise.de/foren/go.shtml?r...forum_id=44546]


    Das Ganze vielleicht nochmal im Überblick:

    Makefile
    Code:
    CC = gcc
    CPP = g++
    OBJ= test.o wrapper.o cplusplus.o
    BIN= test
    
    $(BIN):$(OBJ)
    	$(CPP) -o $(BIN) $(OBJ)
    
    test.o:	test.c wrapper.h cplusplus.h 
    	$(CC) -c  test.c
    
    wrapper.o:	wrapper.cpp wrapper.h
    	$(CPP) -c  wrapper.cpp
    
    cplusplus.o:	cplusplus.cpp cplusplus.h
    	$(CPP) -c  cplusplus.cpp
    
    clean:
    	rm -f *.o $(BIN)

    cplusplus.h
    Code:
    #ifndef _cplusplus_h
    #define _cplusplus_h
    
    class Cpp
    {
    	public:
    	Cpp(int i) : m_num(i) {}
    	
    	int num();
    	
    	private:
    	int m_num;
    };
    
    #endif

    cplusplus.cpp
    Code:
    #include "cplusplus.h"
    #include <iostream>
    using namespace std;
    
    int Cpp::num() {
    	return m_num;
    }

    wrapper.h
    Code:
    #ifndef _WRAPPER_H_
    #define _WRAPPER_H_
    
    struct Cpp;
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif /* ifdef __cplusplus */
    
    struct Cpp* createCpp(int);
    int getNum(struct Cpp*);
    void deleteCpp(struct Cpp*);
    
    #ifdef __cplusplus
    }
    #endif /* ifdef __cplusplus */
    
    
    #endif

    wrapper.cpp
    Code:
    #include "wrapper.h"
    #include "cplusplus.h"
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif /* ifdef __cplusplus */
    
    struct Cpp* createCpp(int n) {
    	return new Cpp(n);
    }
    
    int getNum(struct Cpp* temp) {
    	return temp->num();
    }
    
    void deleteCpp(struct Cpp* temp) {
    	delete temp;
    }
    
    #ifdef __cplusplus
    }
    #endif /* ifdef __cplusplus */

    test.c
    Code:
    #include <stdio.h>
    #include "wrapper.h"
    
    
    int main(int argc, char** argv) {
    	struct Cpp *test;
    
    	test = createCpp(5);
    	printf("Test: %d\n", getNum(test));
    	deleteCpp(test);
    
    	return 0;
    }

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •