PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C-Interface auf C++-Klasse?



Nuke
14-07-2006, 21:57
Hi.

Wenn ich jetzt z.B. folgende Klasse in einer Bibliothek habe:



class MyClass
{
private:
int myInt;

public:
MyClass();
void setMyInt(int theInt);
};


kann ich dazu ein C-Interface machen, so das ich quasi so darauf zugreifen kann:



ClassID *cid = createClass();
setInt(cid,10);
deleteClass(cid);


Aber halt so das es ein reiner C-Compiler auch frisst?!?! ;) Ist das irgendwie möglich, wenn ja, wie? ;)

Danke. :)

locus vivendi
15-07-2006, 14:21
Du kannst im Netz ja mal nach << extern "C" >> suchen. Und in der C++-Faq den Abschnitt über das Mixen von C und C++ lesen.

http://www.parashift.com/c++-faq-lite/

Nuke
16-07-2006, 16:34
Hi.

Ich habe jetzt folgendes:

MyClass.h


#ifndef MYCLASS_H
#define MYCLASS_H

#ifdef __cplusplus
class MyClass {
private:
int myInt;
public:
MyClass();
void showMyInt();
void setMyInt(int a);
};
#else
typedef struct MyClass MyClass;
#endif

#ifdef __cplusplus
extern "C" {
#endif

extern MyClass* cpp_createNewClass();
extern void cpp_showInt(MyClass*);
extern void cpp_deleteClass(MyClass*);

#ifdef __cplusplus
}
#endif
#endif


MyClass.cpp


#include "MyClass.h"
#include <iostream>

MyClass::MyClass()
{
MyClass::myInt = 5;
}

void MyClass::showMyInt()
{
std::cout << myInt << std::endl;
}

void MyClass::setMyInt(int a)
{
MyClass::myInt = 10;
}

// "C"-Funktionen
MyClass* cpp_createNewClass()
{
MyClass *test = new MyClass();
return test;
}

void cpp_showInt(MyClass *mcl)
{
mcl->showMyInt();
}

void cpp_deleteClass(MyClass *mcl)
{
delete mcl;
}


So. Das Ganze dann ein mal mit g++ -c MyClass.cpp kompiliert und dann mit ar zu einem .a gemacht.

Nun habe ich eine main.c erstellt.


#include "MyClass.h"

int main()
{
MyClass *test = cpp_createNewClass();
cpp_showInt(test);
cpp_deleteClass(test);
}


Damit das jetzt funktioniert, muss ich gcc aber die stdc++ mit zum Linken geben. Das liegt wohl an der Bibliothek, da diese die STL-Funktionen ja nicht enthält. Wäre das dann trotzdem so korrekt?

locus vivendi
16-07-2006, 17:28
Ich hätte das schon gleich schreiben können, habe es aber vergessen. Ich würde dazu raten keine Ausnahmen aus den C++-Funktionen nach außen dringen zu lassen. Also z.B. statt

MyClass* cpp_createNewClass()
{
MyClass *test = new MyClass();
return test;
}
lieber

MyClass* cpp_createNewClass()
{
MyClass* test = 0;
try { test = new MyClass(); }
catch([...]) { [...] } // Eine Art von Ausnahmen erkennen,
catch([...]} { [...] } // andere Art.

return test;
}
, und dann in die Catch-Blöcke irgendetwas machen das die Aufrufer feststellen können, was schiefgegangen ist. (nur als Beispiel errno setzen).

Zwar kann man den Gcc auch so benutzen, dass C-Kompilate ebenfalls Ausnahmen weiterleiten können, aber ich würde trotzdem eher eine Methode wählen, mit der C-Code was anfangen kann.


Damit das jetzt funktioniert, muss ich gcc aber die stdc++ mit zum Linken geben. Das liegt wohl an der Bibliothek, da diese die STL-Funktionen ja nicht enthält.
Wenn du eine .so bäckst, kannst du auch den Linker anweisen, dass weitere Shared-Objects automatisch mitgeladen werden. Die Libstdc++ wird übrigens in den meisten Fällen benötigt, auch wenn man gar keine Funktionen der STL, oder des Rests der Standardbibliothek direkt verwendet.


Wäre das dann trotzdem so korrekt?
Mir fällt zumindest nichts problematisches auf. Keine Garantie natürlich.

Eine kleine Sache, nur zum Stil allerdings, nicht zur Korrektheit. Du verwendest die Schreibweise MyClass::Member. Wenn ich sowas lese, erwarte ich das Member irgendetwas wäre was die Angabe MyClass:: benötigt, oder ein statischer Member ist. Wenn du einfach nur andeuten willst, das "Member" ein Member ist, könntest du z.B. schreiben this->Member.

Nuke
16-07-2006, 18:12
OK. Vielen Dank :)

RHBaum
17-07-2006, 10:11
Das mit der per compileranweisungen definierten Klassendefinition, find ich noch ned ganz so toll ....

solange jemand mitm c-client draufzugreift, kein thema.
wenn er aber mit nem c++ client die c schnittstelle bedient, kommt er vielleicht irgendwann auf die idee, die klassendefinition zu nutzen, da die ja in der .h bei ist. was bei unterschiedlichen c++ compilern gruendlich in die hose gehen wuerde ^^

Also schmeiss deine definition der Klasse aus dem allgemeinen header raus, und ersetze die durchn HANDLE typ.

typedef void * HANDLET;

und verwende das als parameter rueckgabewerte fuer deine funktionen.
den void* brauchst dann innerhlaab deiner c++ implementation nur wieder auf die interne Klassendefinition zu casten ....

Ciao ...

anda_skoa
17-07-2006, 15:30
Das mit dem struct Typedef ist IMHO besser, weil der Caste entfällt.
Man kann ja die Typedef Deklaration in einem anderen Header unterbringen, praktisch in der C API zur Bibliothek

Ciao,
_

RHBaum
18-07-2006, 17:06
die erste frage ist, will er das der client mit den Daten die er als zeiger bekommt, ueberhaupt was inhaltlisches anfangen darf ?

wenn nein, wuerd ich den typ verstecken (void * )
wenn ja, wuerd ich die informationen teil der schnittstelle werden lassen, da c gefordert ist, ne plain struct.
Und fuer die C++ schnittstelle nen wrapper fuer die struct bauen ....

Was ich aber nicht machen wuerd, per compilerdirektive die struct einmal als plain strukt fuer c deklarieren, oder andermal fuer c++ als ausgebildete klasse.

Zumindest die c++ klasse gewinnt dann designtechnisch sicher ned den schoenheitswettbewerb. ^^ und ob die compiler dann das selbe draus machen ... k.a.

Ciao ...

locus vivendi
19-07-2006, 15:17
die erste frage ist, will er das der client mit den Daten die er als zeiger bekommt, ueberhaupt was inhaltlisches anfangen darf ?

wenn nein, wuerd ich den typ verstecken (void * )
wenn ja, wuerd ich die informationen teil der schnittstelle werden lassen, da c gefordert ist, ne plain struct.
Ich halte es da auch wie anda_skoa. Mit dem typedef hat Nuke wennigstens egrundsätzlich Typsicherheit. Man könnte von mir aus aber auch für C soetwas machen:
typedef struct ClassHandle_
{
void* p;
} ClassHandle;



Und fuer die C++ schnittstelle nen wrapper fuer die struct bauen ....
Das halte ich ebenfalls für keine besonders gute Idee. So wie Nuke es gemacht hat, kann er die Bibliothek in C++ implementiern. Wenn es nur ein Wrapper sein sollte, müsste er sie in C implementieren. Außerdem müsste der C++-Benutzer dann zwei Typen kennen.


Zumindest die c++ klasse gewinnt dann designtechnisch sicher ned den schoenheitswettbewerb. [...]
Warum? Die Klasse verändert sich doch überhaupt gar nicht dadurch.

anda_skoa
20-07-2006, 12:11
void* ist einfach nicht aussage kräftig, ein opaque handle mittels Typedef ist immer als spezieller Typ erkennbar.

Wenn man sich Sorgen macht, daß des ein Namensproblem gibt, kann man ja für den C Typ einen anderen Namen wählen.

Ciao,
_

RHBaum
20-07-2006, 13:47
Wenn es nur ein Wrapper sein sollte, müsste er sie in C implementieren.
Noe wieso, ich bau hier oft libs die ne c-schnittstelle haben (in VB^^ C und Pascal und compilerabhaengig nutzbar) und hab die implementierung in reinem c++ . die schnittstelle darf nur nix davon mitkriegen ^^


void* ist einfach nicht aussage kräftig, ein opaque handle mittels Typedef ist immer als spezieller Typ erkennbar.

naja, typedeffen iss ja ok, bekommst aber nichts anderes als nen besseren namen dazu. richtige typsicherheit bekommst durch den typedef nich.
und nen void * soll ja auch keine informationen enthalten, das ist ja auch grad die frage. Was soll der client von dem Wert wissen ?

Ciao ...

anda_skoa
22-07-2006, 18:18
Der Client braucht über den Wert nichts zu wissen, es heißt ja auch opaque handle, also undurchsichtig.

Aber der Entwickler hat etwas das er eindeutig identifieren kann.

Ciao,
_