Dll Kompatibilität zwischen Compilern

7

Gibt es eine Möglichkeit, C ++ - DLLs mit verschiedenen kompatiblen Compilern zu erstellen? Die Klassen können Factory-Methoden zum Erzeugen und Löschen haben, so dass jeder Compiler sein eigenes neues / delete verwenden kann (da verschiedene Laufzeiten eigene Heaps haben).

Ich habe den folgenden Code ausprobiert, aber er stürzte bei der ersten Methode ab:

interface.h

%Vor%

test.cpp kompiliert mit VC9, test.exe

%Vor%

a.cpp mit g ++ kompiliert g ++. exe -shared c.cpp -o c.dll

%Vor%

BEARBEITEN: Ich fügte der GCC-CreateClass-Methode die folgende Zeile hinzu: Der Text wurde korrekt auf die Konsole gedruckt, so dass der Funktionsaufruf dealsatled ist.

%Vor%

Ich habe mich gefragt, wie COM es schafft, die Binärkompatibilität sogar sprachübergreifend zu erhalten, da es im Grunde alle Klassen mit der Vererbung (wenn auch nur einzelnen) und damit virtuellen Funktionen gibt. Ich bin nicht ernsthaft gestört, wenn ich die Operatoren / Funktionen nicht überlasten kann, solange ich die grundlegenden OOP-Sachen (dh Klassen und einzelne Erbschaft) beibehalten kann.

    
Fire Lancer 13.01.2009, 18:23
quelle

9 Antworten

8

Sie sollten in der Lage sein, Module mit verschiedenen Compilern zu kombinieren, wenn Sie Ihre Erwartungen senken und sich an einfache Funktionen halten.

Die Art und Weise, wie sich Klassen und virtuelle Funktionen verhalten, wird vom C ++ - Standard definiert, aber die Art und Weise, die implementiert wird, ist Sache des Compilers. In diesem Fall weiß ich, dass VC ++ Objekte erstellt, die virtuelle Funktionen mit einem "vtable" -Zeiger in den ersten 4 Bytes des Objekts haben (ich nehme 32-Bit an), und das verweist auf eine Tabelle von Zeigern auf den Methodeneintrag Punkte.

Also die Zeile: dllclass->PrintSomething(); ist eigentlich äquivalent zu:

%Vor%

Wenn der g ++ - Compiler die virtuellen Funktionstabellen in irgendeiner Weise anders als MSFT VC ++ implementiert - wie es frei ist und immer noch dem C ++ - Standard entspricht - stürzt es einfach ab, wie Sie gezeigt haben. Der VC ++ - Code erwartet, dass sich die Funktionszeiger an bestimmten Stellen im Speicher befinden (relativ zum Objektzeiger).

Es wird komplizierter durch Vererbung und wirklich, wirklich, kompliziert mit Mehrfachvererbung und virtueller Vererbung.

Microsoft hat sehr öffentlich darüber gesprochen, wie VC ++ Klassen implementiert, so dass Sie Code schreiben können, der davon abhängt. Zum Beispiel haben viele von MSFT verteilte COM-Objektheader sowohl C- als auch C ++ - Bindungen im Header. Die C-Bindungen stellen ihre Vtable-Struktur wie mein Code oben dar.

Andererseits hat GNU - IIRC - die Möglichkeit offen gelassen, verschiedene Implementierungen in verschiedenen Releases zu verwenden und nur zu garantieren, dass die mit seinem Compiler erstellten Programme (nur!) dem Standardverhalten entsprechen,

>

Die kurze Antwort besteht darin, bei einfachen C-Stil-Funktionen, POD-Strukturen (Plain Old Data; d.h. keine virtuellen Funktionen) und Zeigern auf undurchsichtige Objekte zu bleiben.

    
Die in Sente 13.01.2009, 20:17
quelle
5

Sie fragen fast sicher nach Ärger, wenn Sie dies tun - während andere Kommentatoren korrekt sind, dass die C ++ - ABI in einigen Fällen identisch sein kann, verwenden die zwei Bibliotheken unterschiedliche CRTs, verschiedene Versionen der STL, unterschiedliche Ausnahmen Semantik, verschiedene Optimierungen ... du gehst einen Weg in Richtung Wahnsinn.

    
Paul Betts 13.01.2009 21:35
quelle
3

Ich denke, Sie finden diesen MSDN Artikel nützlich

Wie auch immer, von einem kurzen Blick auf Ihren Code kann ich Ihnen sagen, dass Sie keinen virtuellen Destruktor in einer Schnittstelle deklarieren sollten. Stattdessen müssen Sie delete this innerhalb von A :: Release () ausführen, wenn der ref count auf Null fällt.

    
Nemanja Trifunovic 13.01.2009 21:18
quelle
3

Eine Möglichkeit, den Code zu organisieren, besteht darin, Klassen sowohl in der App als auch in der DLL zu verwenden, aber die Schnittstelle zwischen den beiden als externe "C" -Funktionen zu behalten. So habe ich es mit C ++ - DLLs gemacht, die von C # -Assemblys verwendet werden. Die exportierten DLL-Funktionen werden verwendet, um Instanzen zu manipulieren, die über statische Methoden der Klasse * Instance () wie folgt zugänglich sind:

%Vor%

Bei mehreren Instanzen von Objekten muss eine dll-Funktion die Instanz erstellen und den Bezeichner zurückgeben, der dann an die Instance () -Methode übergeben werden kann, um das benötigte spezifische Objekt zu verwenden. Wenn Sie eine Vererbung zwischen der App und der DLL benötigen, erstellen Sie auf der App-Seite eine Klasse, die die exportierten DLL-Funktionen umschließt und Ihre anderen Klassen daraus ableitet. Wenn Sie Ihren Code so organisieren, wird die DLL-Schnittstelle zwischen beiden Compilern und Sprachen einfach und portabel gehalten.

    
Tim Butterfield 13.01.2009 22:08
quelle
2

Entscheidend ist, dass das V-Tabellen-Layout zwischen VC und GCC kompatibel ist. Das ist wahrscheinlich in Ordnung. Stellen Sie sicher, dass die Aufrufkonvention übereinstimmt, was Sie überprüfen sollten (COM: __stdcall, you: __thiscall).

Bezeichnenderweise erhalten Sie ein AV zum Schreiben. Es wird nichts geschrieben, wenn Sie die Methode selbst aufrufen, daher ist es wahrscheinlich, dass der Operator & lt; & lt; macht die Bombardierung. Wird std :: cout wahrscheinlich von der GCC-Laufzeit initialisiert, wenn eine DLL mit LoadLibrary () geladen wird? Der Debugger sollte es sagen.

    
Hans Passant 13.01.2009 20:08
quelle
2

Sie können solange extern "C" functions verwenden.

Dies liegt daran, dass das "C" ABI gut definiert ist, während das C ++ ABI absichtlich nicht definiert ist. Somit darf jeder Compiler seinen eigenen definieren.

In einigen Compilern erzeugt die C ++ ABI zwischen verschiedenen Versionen des Compilers oder sogar mit verschiedenen Flags einen inkompatiblen ABI.

    
Martin York 13.01.2009 20:43
quelle
0

interessant .. was passiert, wenn Sie die DLL in VC ++ auch kompilieren, und was ist, wenn Sie einige Debug-Anweisungen in CreateClass () setzen?

Ich würde sagen, dass es möglich ist, dass Ihre zwei verschiedenen Laufzeitversionen von cout im Gegensatz zu Ihrem Methodenaufruf in Konflikt stehen - aber ich vertraue darauf, dass der zurückgegebene Funktionszeiger / dllclass nicht 0x00000004 ist?

    
gbjbaanb 13.01.2009 18:58
quelle
0

Ihr Problem besteht darin, ABI beizubehalten. Obwohl derselbe Compiler, aber verschiedene Versionen, möchten Sie immer noch die ABI beibehalten. COM ist eine Möglichkeit, es zu lösen. Wenn Sie wirklich verstehen möchten, wie COM das löst, lesen Sie diesen Artikel CPP to COM in msdn Das beschreibt die Essenz von COM.

Abgesehen von COM gibt es andere (eine der ältesten) Möglichkeiten, um ABI zu lösen, wie die Verwendung von einfachen alten Daten und undurchsichtigen Zeigern. Schauen Sie auf die Entwickler von Qt / KDE-Bibliotheken, um ABI zu lösen.

    
Gnu Engineer 11.05.2012 21:40
quelle
0

Das Problem mit Ihrem Code, der den Absturz verursacht, sind virtuelle Destruktoren in der Schnittstellendefinition:

%Vor%

Entferne sie und alles wird gut. Das Problem wird durch die Art verursacht, in der die virtuellen Funktionstabellen organisiert sind. MSVC Compiler ignoriert den Destruktor, aber GCC fügt es als eine erste Funktion in der Tabelle hinzu.

Sehen Sie sich die COM-Schnittstellen an. Sie haben keine Konstruktoren / Destruktoren. Definieren Sie niemals Destruktoren in der Schnittstelle und es wird in Ordnung sein.

    
Artyom Chirkov 19.02.2016 04:26
quelle