2009-12-10 11 views
8

Ich habe eine DLL, die auf Daten zugreifen muss, die in STL-Containern in der Host-Anwendung gespeichert sind. Da C++ keine Standard-ABI hat und ich verschiedene Compiler unterstützen möchte, muss die Schnittstelle zwischen der Anwendung und der DLL im Grunde einfach-alte-Daten bleiben.Wie stellt man die STL-Liste über die DLL-Grenze frei?

Für Vektoren ist dies relativ einfach. Sie können einfach den Speicherblock des Vektors zurückkehren, weil es garantiert contigious sein:

// To return vector<int> data 
virtual void GetVectorData(const int*& ptr, size_t& count) const 
{ 
    if (!vec.empty()) 
     ptr = &(vec.front()); 

    count = vec.size(); 
} 

Nun ist die DLL kann sicher Nur-Lese-Zugriff auf die Daten des Vektors über diese Schnittstelle. Die DLL kann dies auch so umschließen, dass sie den Inhalt für sich selbst in einen Vektor kopiert.

Was ist jedoch mit STL-Listen (und Deques)? Gibt es einen anderen direkten Weg, um den Zugriff über eine DLL-Grenze zu ermöglichen? Oder muss ich auf eine Art GetFirst()/GetNext() Schnittstelle zurückgreifen? Ich könnte das für viele Listen tun müssen, also wäre es nett, eine Lösung so einfach wie Vektoren zu haben.

Antwort

7

Vielleicht können Sie etwas wie "Handles" übergeben, um Iteratoren aufzulisten/zu entschlüsseln? Diese Handle-Typen wären undurchsichtig und in einer Header-Datei deklariert, die Sie an die Benutzer versenden würden. Intern müssten Sie die Handle-Werte List/Deque-Iteratoren zuordnen. Grundsätzlich wäre schreiben die Benutzer-Code wie:

ListHandle lhi = GetListDataBegin(); 
const ListHandle lhe = GetListDataEnd(); 

while (lhi != lhe) 
{ 
    int value = GetListItem(lhi); 
    ... 
    lhi = GetNextListItem(lhi); 
} 
1

die Schnittstelle zwischen der Anwendung und DLL hat im Grunde plain-old-Daten zu bleiben.

Nicht unbedingt. Sie müssen sicher sein, dass die gleiche Compiler-Version verwendet wird. Build-Einstellungen, die sich auf das Layout der STL-Objekte auswirken, sind zwischen DLL und Anwendung identisch.

Wenn Sie die DLL in die Wildnis freizugeben, sind Sie richtig besorgt, STL über DLL-Grenzen offen zu legen. Wenn Sie jedoch alles unter Ihrer Kontrolle haben und rein intern sind (oder wenn Sie Build-Einstellungen/Compiler von Drittanbietern streng durchsetzen können), sollten Sie in Ordnung sein.

+0

Sie haben Recht, dass ich damit durchkommen könnte, wenn alle Kompilierungseinstellungen gleich sind. Aber das ist für eine Plugin-Architektur und ich würde gerne verschiedene Compiler unterstützen. Ich habe die Frage bearbeitet, um dies zu klären. – AshleysBrain

+0

Dem stimme ich zu, aber vielleicht sollten Sie betonen, dass es nicht nur das Layout ist - der Code muss mit der gleichen Version des Compilers kompiliert worden sein, damit Implementierungen von Methoden wie new und delete übereinstimmen. –

+0

Wenn Sie Build-Einstellungen/Compiler-Versionen streng erzwingen und alles unter Ihrer Kontrolle steht, was ist dann der Vorteil von DLLs im Vergleich zur Verwendung von statischen Bibliotheken und der Verknüpfung von allem zu einer ausführbaren Datei? –

10

Sie stl Objekte zwischen DLLs passieren können und verschiedene Compiler unterstützen, wenn Sie vorsichtig sind, wo Sie jeden stl Typ instanziiert. Sie benötigen einige intelligente "DLLEXPORT" -Makros - ich verwende den folgenden Satz, um VC und GCC erfolgreich zu unterstützen.

#ifdef WIN32 
#ifdef MYDLLLIB_EXPORTS  // DLL export macros 
#define MYDLLLIB_API __declspec(dllexport) 
#define MYDLLLIB_TEMPLATE 
#else 
#define MYDLLLIB_API __declspec(dllimport) 
#define MYDLLLIB_TEMPLATE extern 
#endif 
#else      // Not windows --- probably *nix/bsd 
#define MYDLLLIB_API 
#ifdef MYDLLLIB_EXPORTS 
#define MYDLLLIB_TEMPLATE 
#else 
#define MYDLLLIB_TEMPLATE extern 
#endif 
#endif // WIN32 

Wenn Sie Ihre DLL kompilieren, definieren Sie MYDLLLIB_EXPORTS. In der DLL können Sie dann jeden stl Typ instanziiert Sie verwenden möchten, zum Beispiel Listen oder Vektoren von Strings

MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::vector<std::string>; 
MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::list<std::string>; 

Verbraucher Ihrer DLL (die nicht über MYDLLLIB_EXPORTS definiert) werden dann sehen

extern template class __declspec(dllimport) std::vector<std::string>; 

und verwenden Sie den aus Ihrer DLL exportierten Binärcode, anstatt ihn selbst zu instanziieren.

+0

Das ist eine sehr interessante Lösung - müsste ich das gleiche für std :: vector tun :: iterator und std :: list :: iterator? Ist es für die DLL, die STL-Klassen von einer Host-EXE verwendet, gleich? – AshleysBrain

+0

Sie müssen die Iteratoren nicht instanziieren, da Sie beim Instanziieren einer bestimmten STL-Containerklasse auch Code für alle Klassen innerhalb der Containerklasse erhalten. Ich weiß nicht, wie ich STL-Klassen von einem Host-EXE-System aus verwenden soll - ich habe das selbst nicht versucht. Wenn Sie also dieser Methode folgen wollen, müssen Sie ein wenig mit einem einfachen Test experimentieren. – mcdave

+2

Hier ist es in MSDN: http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q168958 – k06a