C++-Klassen- oder Struct-Kompatibilität mit C-Struct
Lesezeit: 9 Minuten
Cem Kalyoncu
Ist es möglich, eine C++-Klasse oder -Struktur zu schreiben, die vollständig mit C-Struktur kompatibel ist? Mit Kompatibilität meine ich Größe des Objekts und Speicherorte der Variablen. Ich weiß, dass es böse ist, es zu benutzen *(point*)&pnt oder auch (float*)&pnt (in einem anderen Fall, in dem Variablen Gleitkommazahlen sind), aber bedenken Sie, dass dies aus Gründen der Leistung wirklich erforderlich ist. Es ist nicht logisch, den regulären Casting-Operator millionenfach pro Sekunde zu verwenden.
Nehmen Sie dieses Beispiel
Class Point {
long x,y;
Point(long x, long y) {
this->x=x;
this->y=y;
}
float Distance(Point &point) {
return ....;
}
};
Die C-Version ist eine POD-Struktur
struct point {
long x,y;
};
Zu welchem Zweck möchten Sie diese Kompatibilität? Möchten Sie Binärdaten von einem C++-Programm an ein AC-Programm übergeben, oder möchten Sie ein C++-Objekt an eine AC-Funktion übergeben?
– Dima
15. September 2009 um 20:39 Uhr
“Aber bedenken Sie, dass dies aus Gründen der Leistung wirklich erforderlich ist. Es ist nicht logisch, einen regulären Typ-Casting-Operator millionenfach pro Sekunde zu verwenden.” Das ergibt für mich keinen Sinn.
– GManNickG
15. September 2009 um 20:45 Uhr
@Dima: zu C-Funktion @GMan: nicht-böse Methode sollte Type-Casting-Operator definieren operator point() { point p; px=dies->x; py=this.y; gib p zurück; } Allerdings werde ich diese Punkte in zwei Fällen verwenden, die int eingegeben werden, um die Windows-Funktion zu übergeben, um die Cursorposition zu finden. Float-Typ wird als Vertices an OpenGL gesendet (während es in Float-Array konvertiert wird), zuerst wird einer pro Frame (350 Mal/Sek.) ausgeführt, der andere ist für 4 x gezeichnete Objekte x 350. Sie werden innerhalb eines Spiels verwendet Motor.
– Cem Kalyoncu
15. September 2009 um 22:33 Uhr
Stephen C. Stahl
Am saubersten war es, von der C-Struktur zu erben:
struct point
{
long x, y;
};
class Point : public struct point
{
public:
Point(long x, long y)
{ this->x=x; this->y=y; }
float Distance(Point &point)
{ return ....; }
}
Der C++-Compiler garantiert, dass der Strukturpunkt im C-Stil dasselbe Layout wie beim C-Compiler hat. Die C++-Klasse Point erbt dieses Layout für ihren Basisklassenteil (und da sie keine Daten oder virtuellen Elemente hinzufügt, hat sie dasselbe Layout). Ein Zeiger auf die Klasse Point wird ohne Umwandlung in einen Zeiger auf den Strukturpunkt konvertiert, da die Konvertierung in einen Basisklassenzeiger immer unterstützt wird. Sie können also Point-Objekte der Klasse verwenden und Zeiger auf sie frei an C-Funktionen übergeben, die einen Zeiger auf den Strukturpunkt erwarten.
Wenn es bereits eine C-Header-Datei gibt, die den Strukturpunkt definiert, können Sie diese natürlich einfach einschließen, anstatt die Definition zu wiederholen.
Dies ist, was ich gefunden habe. Vielen Dank!
– Äonil
17. März 2011 um 5:19 Uhr
Dies kann zu einem schrecklichen Durcheinander führen, da die Strukturmitglieder öffentlich sind. Es bricht im Grunde jede Kapselung vollständig.
– Falscher Name
15. Juni 2015 um 23:40 Uhr
@FakeName Das ist inhärent, wenn C beteiligt ist, siehe stackoverflow.com/questions/2522424/… Und Sie können mindestens den C++-Punkt kapseln, wenn Sie Ihre Vererbung durchführen protected anstelle von public. Siehe auch isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c
– Antonio
6. Oktober 2016 um 12:45 Uhr
Jason Williams
Ja.
Verwenden Sie in beiden Sprachen dieselben Typen in derselben Reihenfolge
Stellen Sie sicher, dass die Klasse nichts Virtuelles enthält (damit kein vtable-Zeiger auf der Vorderseite hängen bleibt).
Abhängig von den verwendeten Compilern müssen Sie möglicherweise das Strukturpacken (normalerweise mit Pragmas) anpassen, um die Kompatibilität zu gewährleisten.
(bearbeiten)
Außerdem müssen Sie darauf achten, die sizeof() der Typen mit Ihren Compilern zu überprüfen. Zum Beispiel bin ich auf einen Compiler gestoßen, der Kurzschlüsse als 32-Bit-Werte gespeichert hat (wobei die meisten 16 verwenden). Ein häufigerer Fall ist, dass ein int normalerweise 32 Bit auf einer 32-Bit-Architektur und 64 Bit auf einer 64-Bit-Architektur ist.
Nach meiner Erfahrung sind Pack-Pragmas eine schlechte Idee. Wenn Sie die Elementvariablen so anordnen, dass sie eine natürliche Ausrichtung haben, sind Pack-Pragmas nicht erforderlich. Und wenn Du nicht Wenn Sie ihnen eine natürliche Ausrichtung zuweisen und versuchen, Pragmas zu verwenden, um dies zu beheben, können Sie auf zwei Probleme stoßen: 1) Die Pragmas funktionieren immer noch nicht. 2) Sie zwingen den Compiler, etwas falsch auszurichten, was der Prozessor nicht falsch ausrichten möchte Gleitkommazahlen. Zumindest auf PowerPC erzeugt ein falsch ausgerichteter Float einen Interrupt, der von der Software behandelt wird, das kann bedeutend Wirkungsleistung.
– KeyserSoze
15. September 2009 um 22:34 Uhr
Das Definieren eines C++-Objekts mit demselben Layout wie die C-Struktur funktioniert, aber es drückt die Absicht nicht so klar aus wie das Klasseninteresse aus der Struktur (siehe meine Antwort unten).
– Stephen C. Stahl
15. September 2009 um 23:18 Uhr
@keysersoze: Ja, natürliche Ausrichtung ist besser. Einige Compiler werden Daten jedoch immer noch anders packen, weshalb es sinnvoll ist, das Packen zu erwähnen. In der Tat habe ich vergessen zu erwähnen, dass ich auch auf die Mitgliedergrößen achten sollte. Ich werde meinen Beitrag bearbeiten, um diesen Punkt hinzuzufügen.
– Jason Williams
16. September 2009 um 7:10 Uhr
Das gesuchte Wort ist POD-Typen. Sie sind im Standard klar definiert, und im Grunde besteht ihr gesamter Zweck darin, mit C kompatibel zu sein.
– jalf
16. September 2009 um 10:00 Uhr
@jalf: Ja. Ich sah keinen Sinn darin, keyersozes guten Post über POD zu wiederholen. Ich meinte einfach, dass Sie (im allgemeinen Sinne) nur überprüfen müssen, ob die spezifischen Compiler, die Sie verwenden, identische Binärstrukturen erzeugen. Ich würde meinen Code testen, um dies zu überprüfen, unabhängig davon, welche Typen ich verwendet habe.
Sie sollten Ihre POD-Datenstrukturen so entwerfen, dass sie eine natürliche Ausrichtung haben und dann zwischen Programmen weitergegeben werden können, die von verschiedenen Compilern auf verschiedenen Architekturen erstellt wurden. Bei der natürlichen Ausrichtung ist der Speicheroffset eines beliebigen Members durch die Größe dieses Members teilbar. IE: Ein Float befindet sich an einer Adresse, die durch 4 teilbar ist, ein Double befindet sich an einer Adresse, die durch 8 teilbar ist. Wenn Sie ein Zeichen gefolgt von einem Float deklarieren, werden die meisten Architekturen 3 Bytes auffüllen, aber einige könnten möglicherweise 1 Byte auffüllen. Wenn Sie ein Float deklarieren, gefolgt von einem Zeichen, werden alle Compiler (ich sollte eine Quelle für diese Behauptung hinzufügen, sorry) überhaupt nicht auffüllen.
Es gibt keine Garantie dafür, dass der Compiler kein zusätzliches Padding einfügt, also können Sie es nicht sein sicher Es funktioniert, es sei denn, Sie verwenden ein Pack-Pragma oder ähnliches.
– jalf
16. September 2009 um 10:01 Uhr
Ich habe noch nie einen Compiler gesehen, der Padding für Strukturen mit natürlicher Ausrichtung eingefügt hat. VC++ 6.0, 2003, 2005, 2008, Metrowerks für PPC, Metrowerks für HC12, gcc für ARM, gcc 2.98-3.x für PowerPC, gcc 3.x-4.x für x86, SDCC für 8051, Crossworks für MSP430. Ich würde mich sehr über ein Gegenbeispiel oder eine gute Begründung dafür freuen, warum dies so ist.
– KeyserSoze
16. September 2009 um 20:27 Uhr
Wenn Sie ein Pack-Pragma auf Strukturen anwenden, die nicht bereits über eine natürliche Ausrichtung verfügen, treten häufig schädliche Nebenwirkungen auf. Wenn Sie z. B. auf PowerPC falsch ausgerichtete Floats haben, tritt eine Hardware-Ausnahme auf. Der Hardware-Handler kann den Prozessor zurücksetzen (üblich bei eingebetteten Apps) oder das Problem in der Software behandeln (was SLOOOOW ist). In beiden Fällen kann Ihnen die Verwendung eines Pack-Pragmas nur schaden, Sie möchten die Struktur auf jeden Fall so definieren, dass sie eine natürliche Ausrichtung hat, und kein Pack verwenden, damit es auf allen Plattformen gleich funktioniert.
– KeyserSoze
16. September 2009 um 20:28 Uhr
C und C++ sind unterschiedliche Sprachen, aber es war schon immer die Absicht von C++, dass Sie eine Implementierung haben können, die beide Sprachen binärkompatibel unterstützt. Da es sich um verschiedene Sprachen handelt, ist es immer ein Detail der Compiler-Implementierung, ob dies tatsächlich unterstützt wird. Typischerweise unterstützen Anbieter, die sowohl einen C- als auch einen C++-Compiler (oder einen einzelnen Compiler mit zwei Modi) bereitstellen, die vollständige Kompatibilität für die Übergabe POD-Strukturen (und Hinweise auf POD-Strukturen) zwischen C++-Code und C-Code.
Oft wird die Garantie durch bloßes Vorhandensein eines benutzerdefinierten Konstruktors gebrochen, obwohl Sie manchmal einen Zeiger auf ein solches Objekt an eine C-Funktion übergeben können, die einen Zeiger auf a erwartet struct mit und identischer Datenstruktur und es wird funktionieren.
Kurz gesagt: Überprüfen Sie Ihre Compiler-Dokumentation.
Adisak
Verwenden Sie in C und C++ dasselbe “struct”. Wenn Sie Methoden in der C++-Implementierung hinzufügen möchten, können Sie die Struktur erben, und die Größe sollte gleich sein, solange Sie keine Datenelemente oder virtuellen Funktionen hinzufügen.
Beachten Sie, dass eine leere Struktur oder Datenmember, die leere Strukturen sind, in C und C++ unterschiedliche Größen haben. In C ist sizeof(empty-struct) == 0, obwohl in C99 leere Strukturen nicht erlaubt sein sollen (aber möglicherweise trotzdem als “Compiler-Erweiterung” unterstützt werden). In C++ ist sizeof(empty-struct) != 0 (typischer Wert ist 1).
tragomaskhalos
Zusätzlich zu anderen Antworten würde ich sicher sein, keine Zugriffsspezifizierer (öffentlich:, privat: usw.) in Ihre C++-Klasse/Struktur einzufügen. Der IIRC-Compiler darf Blöcke von Member-Variablen entsprechend der Sichtbarkeit neu anordnen, so dass private: int a; pubic: int b; könnte a und b vertauscht bekommen. Siehe zB diesen Link: http://www.embedded.com/design/218600150?printable=true
Ich gebe zu, dass ich verblüfft bin, warum die Definition von POD kein entsprechendes Verbot enthält.
Solange Ihre Klasse nicht einige fortgeschrittene Merkmale ihrer Art aufweist, wie z. B. das Wachsen von etwas Virtuellem, sollte es ziemlich dieselbe Struktur sein.
Außerdem kannst du wechseln Class (was aufgrund der Großschreibung sowieso ungültig ist) zu struct ohne Schaden anzurichten. Abgesehen davon, dass die Mitglieder öffentlich werden (sie sind jetzt privat).
Aber jetzt, wo ich daran denke, dass Sie von Typumwandlung gesprochen haben … Es gibt keine Möglichkeit, sich umzudrehen float hinein long den gleichen Wert darstellen oder umgekehrt, indem der Zeigertyp gecastet wird. Ich hoffe, Sie wollen diese Hinweise nur, um Dinge zu verschieben.
Wenn Sie struct in class ändern, wird die nur Der Effekt besteht darin, dass die Standardschutzstufe (bis zum ersten expliziten Aufruf von public:, private: oder protected:) öffentlich statt privat wird.
– bdonlan
15. September 2009 um 20:47 Uhr
bdonlan, ja, das habe ich erwähnt. Aber man sollte die emotionale Seite des Deals nicht unterschätzen 😉
– Michael Krelin – Hacker
15. September 2009 um 20:49 Uhr
Float-Version ist ein völlig anderer Fall (OpenGL) und int ist für Windows (Punktstruktur). und Klassenbeispiel sollte public: at top haben. Ich werfe es nur als Probe.
– Cem Kalyoncu
15. September 2009 um 20:59 Uhr
okay, wollte nur sichergehen, dass du es nicht versuchst Konvertieren von Float auf diese Weise.
– Michael Krelin – Hacker
15. September 2009 um 21:12 Uhr
10189700cookie-checkC++-Klassen- oder Struct-Kompatibilität mit C-Structyes
Zu welchem Zweck möchten Sie diese Kompatibilität? Möchten Sie Binärdaten von einem C++-Programm an ein AC-Programm übergeben, oder möchten Sie ein C++-Objekt an eine AC-Funktion übergeben?
– Dima
15. September 2009 um 20:39 Uhr
“Aber bedenken Sie, dass dies aus Gründen der Leistung wirklich erforderlich ist. Es ist nicht logisch, einen regulären Typ-Casting-Operator millionenfach pro Sekunde zu verwenden.” Das ergibt für mich keinen Sinn.
– GManNickG
15. September 2009 um 20:45 Uhr
@Dima: zu C-Funktion @GMan: nicht-böse Methode sollte Type-Casting-Operator definieren operator point() { point p; px=dies->x; py=this.y; gib p zurück; } Allerdings werde ich diese Punkte in zwei Fällen verwenden, die int eingegeben werden, um die Windows-Funktion zu übergeben, um die Cursorposition zu finden. Float-Typ wird als Vertices an OpenGL gesendet (während es in Float-Array konvertiert wird), zuerst wird einer pro Frame (350 Mal/Sek.) ausgeführt, der andere ist für 4 x gezeichnete Objekte x 350. Sie werden innerhalb eines Spiels verwendet Motor.
– Cem Kalyoncu
15. September 2009 um 22:33 Uhr