Wie kann ich Standard Library (STL) -classn in meiner DLL-Schnittstelle oder ABI verwenden?

Es gab vorher einige Fragen zum Exportieren einer class, die STL-classn in Bezug auf Visual Studio-Warnung C4251 enthält: ZB diese Frage oder diese Frage. Ich habe die exzellente Erklärung bereits bei UnknownRoad gelesen.

Die Warnung blind zu deaktivieren scheint ein wenig gefährlich zu sein, obwohl es eine Option sein könnte. Alle diese Std-classn zu verpacken und diese zu exportieren ist auch nicht wirklich eine Option. Es wird schließlich die Standard Template Library genannt … Dh, man möchte eine Schnittstelle zu diesen Standardklassen bereitstellen.

Wie kann ich STL-classn in meiner DLL-Schnittstelle verwenden? Was sind gängige Praktiken?

   

Denken Sie daran, bevor Sie weiterlesen: Meine Antwort kommt aus der Sicht des Schreibens von portierbarem Code, der in Anwendungen verwendet werden kann, die aus Modulen bestehen, die unter verschiedenen Compilern kompiliert wurden. Dies kann unterschiedliche Versionen oder sogar verschiedene Patch-Level desselben Compilers beinhalten.

Wie kann ich STL-classn in meiner DLL-Schnittstelle verwenden?

Antwort: Sie können oft nicht 1 .

Grund: Die STL ist eine Codebibliothek, keine Binärbibliothek wie eine DLL. Es hat keinen einzigen ABI, der garantiert überall gleich ist. In der Tat steht STL für ” Standard Template Library”, aber ein wichtiges operatives Wort hier neben Standard ist Template .

Der Standard definiert die Methoden und Datenelemente, die jede STL-class bereitstellen muss, und definiert, was diese Methoden tun sollen. aber nicht mehr. Insbesondere spezifiziert der Standard nicht, wie Compiler-Schreiber die Standard-definierte functionalität implementieren sollten. Compiler-Schreiber können eine Implementierung einer STL-class bereitstellen, die Elementfunktionen und Membervariablen hinzufügt, die nicht im Standard aufgeführt sind, solange die Member, die im Standard definiert sind, immer noch dort sind und tun, was der Standard sagt.

Vielleicht ist ein Beispiel in Ordnung. Die basic_string class wird im Standard als bestimmte basic_string definiert. Die eigentliche Definition ist fast 4 Seiten im Standard, aber hier ist nur ein Ausschnitt davon:

 namespace std { template, class Allocator = allocator > class basic_string { [snip] public: // 21.3.3 capacity: size_type size() const; size_type length() const; size_type max_size() const; void resize(size_type n, charT c); void resize(size_type n); size_type capacity() const; void reserve(size_type res_arg = 0); void clear(); bool empty() const; [snip] }; 

Betrachten Sie die Elementfunktionen size() und length() . Es gibt im Standard keine Angaben zu Membervariablen zum Speichern dieser Informationen. Tatsächlich sind überhaupt keine Variablen definiert, nicht einmal die Zeichenfolge selbst. Wie wird das umgesetzt?

Die Antwort ist, viele verschiedene Möglichkeiten. Einige Compiler verwenden möglicherweise eine size_t , um die Größe zu halten, und ein char* , um die Zeichenfolge zu speichern. Ein anderer könnte einen pointers auf einen anderen Datenspeicher verwenden, der diese Daten enthält (dies könnte der Fall in einer Implementierung mit Referenzzählung sein). Tatsächlich können verschiedene Versionen oder sogar Patch-Level desselben Compilers diese Implementierungsdetails ändern. Du kannst dich nicht auf sie verlassen. Die Implementierung von MSVC 10 könnte also wie folgt aussehen:

 namespace std { template, class Allocator = allocator > class basic_string { [snip] char* m_pTheString; }; size_t basic_string::size() const { return strlen(m_pTheString;) } 

… während MSVC 10 mit SP1 so aussehen könnte:

 namespace std { template, class Allocator = allocator > class basic_string { [snip] vector m_TheString; }; size_t basic_string::size() const { return m_TheString.size(); } 

Ich sage nicht, dass sie so aussehen, ich sage, sie könnten es tun . Der Punkt hier ist die tatsächliche Implementierung ist Plattform-abhängig, und Sie können wirklich nicht wissen, was es woanders sein wird.

Jetzt sagen Sie, dass Sie MSVC10 verwenden, um eine DLL zu schreiben, die diese class exportiert:

 class MyGizmo { public: std::string name_; }; 

Was ist die sizeof(MyGizmo) ?

Angenommen, meine vorgeschlagenen Implementierungen werden unter MSVC10 die sizeof(char*) , aber unter SP1 wird es sizeof(vector) . Wenn Sie eine Anwendung in VC10 SP1 schreiben, die die DLL verwendet, wird die Größe des Objekts anders aussehen, als es tatsächlich ist. Die binäre Schnittstelle wurde geändert.


Eine weitere Behandlung hierzu finden Sie in C ++ Coding Standards (Amazon Link ), Ausgabe 63.


1 : ” Sie können es oft nicht ” Sie können Standardbibliothekskomponenten oder andere Komponenten der Codebibliothek (wie Boost) mit einiger Zuverlässigkeit exportieren, wenn Sie die Toolchains und Bibliotheken vollständig kontrollieren können.

Das grundlegende Problem besteht darin, dass bei Quellcode-Bibliotheken die Größen und Definitionen von Dingen zwischen verschiedenen Compilern und verschiedenen Versionen der Bibliothek unterschiedlich sein können. Wenn Sie in einer Umgebung arbeiten, in der Sie diese beiden Dinge überall dort steuern, wo Ihr Code verwendet wird, haben Sie wahrscheinlich kein Problem. Zum Beispiel bei einem Handelsunternehmen, wo alle Systeme intern geschrieben und nur intern verwendet werden, könnte dies möglich sein.