STL in DLL? Ja oder Nein, was macht Sinn?!?!



  • Hallo,
    ich hoffe das ist das passende Forum. Ich konnte mich nicht zwischen dem Visual C++ Forum und diesen entscheiden, aber ich glaube das Problem stellt sich auch mit anderen Compilern, daher lieber das allgemeine c++ Forum.

    Ich arbeite seit ein paar Wochen nun auch mit DLLs. Das muss ich tun, weil ich externe DLLs habe um zum Beispiel Bilder zu verarbeiten und ich sonst Linkerfehler bekomme (doppelter Code, etc). (Beispiel: ImageMagick, etc)

    Also habe ich meine Libs so gebaut, dass ich die als DLL und LIB durch den compiler durchlassen kann und das klappt auch.

    Nunja....bis zu dem Punkt, wo ich irgendwelche STL-Sachen verwende.
    Die einfache Verwendung von

    typedef std::list<MyClass*> MyClassList;
    

    stellt mich nun immer wieder vor Problemen.

    Ich hab jetzt sehr viel gelesen, und es tun sich mehr Fragen auf als Antworten.

    Ich habe gelesen, dass ich die <list> mit exportieren soll. Also grob mach ich das so:

    // DLL?
    #ifdef _WINDLL // Wenn DLL-Exportiert wird
    
    	#define S3D_DLL_PRE __declspec(dllexport)
    	#define S3D_DLL_PRE_TEMPLATE
    
    #else // _WINDLL
    
    	#ifdef _LOADSTATIC // Wenn Klasse statisch geladen werden soll
    		#define S3D_DLL_PRE
    	#else // _LOADSTATIC || Dynamisch laden
    		#define S3D_DLL_PRE __declspec(dllimport)
    		#define S3D_DLL_PRE_TEMPLATE extern
    	#endif // _LOADSTATIC
    
    #endif // _WINDLL
    
    // SLT Exports
    
    #ifdef S3D_DLL_PRE_TEMPLATE
    	#ifdef _DEBUG
    		#define S3D_EXPORT_CLASS_BASE class
    	#else // _DEBUG
    		#define S3D_EXPORT_CLASS_BASE struct
    	#endif // _DEBUG
    
    	// STL::list
    	#define EXPORT_STL_LIST(declspec_, T_) \
    		S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<T_ >; \
    		S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<std::_List_nod<T_, std::allocator<T_ > >::_Node>; \
    		S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<std::_List_nod<T_, std::allocator<T_ > >::_Node*>; \
    		S3D_DLL_PRE_TEMPLATE template class declspec_ std::list<T_ >;
    
    #else // S3D_DLL_PRE_TEMPLATE
    
    	// STL::list
    	#define EXPORT_STL_LIST(declspec_, T_)
    
    #endif // S3D_DLL_PRE_TEMPLATE
    
    // EIGENTLICHE DEFINITION
    class S3dActionBase;
    // Liste für Sammlung aus ActionBase-Klassenzeiger
    #include <list>
    #pragma warning( push )
    #pragma warning( disable : 4231 )
    EXPORT_STL_LIST(S3D_DLL_PRE, S3dActionBase*)
    #pragma warning( pop )
    typedef std::list<S3dActionBase*> TyS3dActionBaseList;
    

    Das hab ich mir aus verschiedenen Forums-Beiträgen zusammen gebastelt.
    Der Kenner sieht gleich: Ich verwende Visual Studio 2008 Professional.

    Unter Debug klappt alles, also dachte ich: Super!
    Jetzt das Manko: Unter Release bekomme ich Fehler über Fehler zur Klasse: "std::_Container_base_aux" die ich auch noch exportieren soll, etc....

    Also habe ich nun das Internet weiter durchsucht. Und ich habe viele Einträge gefunden, die geschrieben haben: "Nein! Tu es blos nicht, keinen Export von STL-Sachen, das führt unweigerlich zu Speicherschutzverletzungen und Hackbaren Code und dein Kühlschrank wird auch noch abgetaut" (Entschuldigung für die Ironie!)

    Ich weiß jetzt bald nicht mehr weiter.

    Ich möchte eigentlich "nur" eine Klasse schreiben, die ich als direkte DLL einbinden kann. Und zwar nicht nachträglich gelinkt, sondern direkt beim Programmstart. Am Besten auch noch so, dass wenn sich die DLL ändert, ich nicht gleich die ganze EXE neu ausliefern muss.

    Das müsste doch nach meinem Verständnis genau dann gehen, wenn ich die STL-Klassen mitliefere, weil dann die Exe ja genau mit den STL-Funktionen arbeiten kann, die ich habe. STL enthält doch als zweites Wort "Template", also ist das Template doch gar nicht als solches in der Klasse, sondern "nur" die Funktionien, auf die meine Inhalte über Template zugreifen???

    Was verstehe ich gerade falsch, denn irgendwie gehen die Meinungen gerade weit auseinander und selbst meine schlauen Bücher über C++ sagen mir nicht wirklich viel aus.

    Bitte um Hilfe und evtl. ein paar gute Anleitungen, zum Thema "Wie-Wo-Warum-Weshalb-Wieso eine DLL und wie mache ich es richtig?!?".

    Vielen Dank,
    In tiefster verzweifelter Unklarheit,
    Stefan



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x) in das Forum Compiler- und IDE-Forum verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Link

    Ganz korrekterweise müsstest du die benutzten Klassen (darunter Template-Instanziierungen der STL) exportieren. Das ist manchmal aber äusserst mühsam, wegen zusätzlicher Default-Templateparameter dürftest du Klassen der Standardbibliothek streng genommen nicht einmal selbst deklarieren.

    Es ist wohl einfacher, die STL nicht zu exportieren. Ich bin auch noch nie einem Fall begegnet, in dem das nicht funktioniert hat.



  • Hallo,
    danke für den Link.
    Also wenn ich das richtig verstanden habe, dann gibt es entweder viel arbeit, oder ein "Warning disable".

    Gut: Ich hab Ergeiz und bin (fast) jung, ich nehm die Arbeit. Außerdem hab ich ja momentan "nur" eine Liste (wird sicherlich noch mehr, aber was soll's) und ich will ja auch was lernen! Es ist zwar nicht umbedingt nur der Weg das Ziel, aber ich würde mit gerne eine Karte erarbeiten.

    Also müsste ich das jetzt ungefähr folgt machen?

    #ifdef _WINDLL // Wenn DLL-Exportiert wird
    
    /*
    #define EXPORT_STL_LIST(declspec_, T_) \
    	S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<T_ >; \
    	S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<std::_List_nod<T_, std::allocator<T_ > >::_Node>; \
    	S3D_DLL_PRE_TEMPLATE template class declspec_ std::allocator<std::_List_nod<T_, std::allocator<T_ > >::_Node*>; \
    	S3D_DLL_PRE_TEMPLATE template class declspec_ std::list<T_ >; 
    */
    
    #include <list>
    class S3D_DLL_PRE TyS3dActionBaseListAllocator
    	: public std::allocator<S3dActionBase*> {};
    class S3D_DLL_PRE TyS3dActionBaseListAllocatorListNode
    	: public std::allocator<std::_List_nod<S3dActionBase* , TyS3dActionBaseListAllocator >::_Node> {};
    class S3D_DLL_PRE TyS3dActionBaseListAllocatorListNodeP
    	: public std::allocator<std::_List_nod<S3dActionBase* , TyS3dActionBaseListAllocator >::_Node*> {};
    class S3D_DLL_PRE TyS3dActionBaseList
    	: public std::list<S3dActionBase*, TyS3dActionBaseListAllocator> {};
    
    #else // _WINDLL
    
    #include <list>
    typedef std::list<S3dActionBase*> TyS3dActionBaseList;
    
    #endif // _WINDLL
    
    class MySammlung
    {
    protected:
      TyS3dActionBaseList MyList;
    
    public:
      void AddToListe(S3dActionBase* ToAdd); // MyList.push_back();
      bool RemoveFromListe(S3dActionBase* ToRemove); // if (find(ToRemove)) {remove());
    }
    

    Das ganze strahlt natürlich noch vor Fehlermeldungen und Zugriffrechtproblemen (protected), aber ich würde gerne kurz nachfragen, ob das der richtige Weg ist?

    Vielen Dank,
    Hoffnungvoll,
    Stefan



  • Die STL hat in einem dll Interface nix verloren, das gibt früher oder später nur Probleme...

    Du solltest dir auch bewusst sein dass das Exportieren von Klassen in dlls nur ein compilerspezifisches Komfortfeature ist und absolut nicht portabel. Die so erzeugten dlls kannst du nur mit MSVC verwenden und du bist auch dort auf load time dynamic linking beschränkt usw.



  • Und warum musst Du dazu STL Klassen exportieren?
    Benutze sie doch einfach. Was musst Du da exportieren?
    Was versprichst Du Dir davon?
    Wenn Du Code-Optimierung willst benutze "Whole Program Optimization" des Compilers/Linkers.

    Achte darauf dass die Libs, DLLs und EXEs garantiert mit dem selben Compiler und SP erzeugt wurden.
    - und -
    Das geht nur, wenn auch die DLL Version der CRT verwendet wird. Deine Header sollten diese Nutzung unbedingt aprüfen und einen #error auswerfen wenn diese Settingsnicht gegeben sind.

    Du bekommst immer dann Probleme mit diesen Klassen wenn statische Member verwendet werden.



  • dot schrieb:

    Die STL hat in einem dll Interface nix verloren, das gibt früher oder später nur Probleme...

    Du solltest dir auch bewusst sein dass das Exportieren von Klassen in dlls nur ein compilerspezifisches Komfortfeature ist und absolut nicht portabel. Die so erzeugten dlls kannst du nur mit MSVC verwenden und du bist auch dort auf load time dynamic linking beschränkt usw.

    OK, wenn das so ist, akzeptier ich das dann. Aber nach wie vor hab ich dann ein Problem:

    class DLL_EXPORT MyClass
    {
    protected:
      std::list<ClassB*> MyListe;
    public:
      void AddToList(ClassB* ToAdd);
    };
    

    Wie löse ich das nun?
    Irgendwie fehlt mir jetzt die "Best Practise" zu dem Thema. Auch der Link von Nexus sagt mir "nur" was ich tun kann, aber gibt jetzt nicht aufschluss auf die Praxis. Es ist doch so, dass 80% der Programmierungen drehen sich um irdenwelche Daten und Verwaltung von Listen. Genau deswegen gibt es die STL ja. Wenn ich die in DLLs nicht verwenden darf/soll, was tu ich dann?

    Sorry, aber ich bekomme gerade keinen klaren Gedanken wie man das nun "richtig" löst.

    1.) Ich soll STL nicht exportieren, weil das zu Problemen mit verschiedenen Comps führen kann (oder gar "nur" mit MSVC klappt?)
    2.) Ich soll die Warning nicht deaktivieren, weil (Zitat aus anderem Thread) "Eine Warnung ja immer einen Grund hat".
    3.) Ich soll die STL nicht in Klassen erben, und mit exportieren, weil das Overhead gibt und nicht kompatibel ist.

    Also was man ich dann?

    Im Endeffekt möchte ich ja die STL-Klassen gar nicht exportieren, sondern nur anwenden. Ich will die nicht mal public machen, sondern "nur" intern in meinen Klassen verwenden und nur über eigenen Klassenfunktionen verarbeiten (also füllen, sotieren, etc).

    Jetzt bitte ich um Hilfe, nach der Frage des Lebens, dem Sinn und überhaupt nach allem....nö...stopp, das war irgend ein anderes Buch mit "Anhaltern" und "Galaxis"..

    Nochmal:
    Ich bitte um Hilfe der "Best Practis". Wie löst man das am sinnvollsten. Ich werde sicherlich keine DLL mit einem Compiler schreiben und diese mit einem anderen Compiler verwenden. Aber was gut wäre ist, wenn ich zur neuen DLL nicht umbedingt eine neue Exe schreiben müsste (also vorrausgesetzt der Header ist identisch).

    Geht das überhaupt sinnvoll, oder gibt es da für jedes Projekt Fallentscheidungen.

    Vielen Dank,
    und sorry dass ich jetzt einfach mal so "grundsätzlich" fragen muss.
    Stefan



  • Martin Richter schrieb:

    Und warum musst Du dazu STL Klassen exportieren?
    Benutze sie doch einfach. Was musst Du da exportieren?
    Was versprichst Du Dir davon?
    Wenn Du Code-Optimierung willst benutze "Whole Program Optimization" des Compilers/Linkers.

    Achte darauf dass die Libs, DLLs und EXEs garantiert mit dem selben Compiler und SP erzeugt wurden.
    - und -
    Das geht nur, wenn auch die DLL Version der CRT verwendet wird. Deine Header sollten diese Nutzung unbedingt aprüfen und einen #error auswerfen wenn diese Settingsnicht gegeben sind.

    Du bekommst immer dann Probleme mit diesen Klassen wenn statische Member verwendet werden.

    Hallo,
    habe deinen Beitrag jetzt eben erst gesehen.
    Was ich mir davon verspreche? Nun eigentlich habe ich in den Tutprials es damals so verstanden dass ich das muss. Wollen tu ich das nicht. Nur wenn es nötig ist.
    Mir ist es das liebste, ich habe "nur" die Endklassen im Export.

    Also verstehe ich dich richtig? Wenn ich die Klassen "nur anwende" dann einfach Warning aus und es ist gut?

    Danke,
    Stefan



  • Wenn Du diese Klasse exportierst müsstest Du kein Problem bekommen.

    class DLL_EXPORT MyClass 
    { 
    protected: 
      std::list<ClassB*> MyListe; 
    public: 
      void AddToList(ClassB* ToAdd); 
    };
    

    Damit musst Du die STL doch nicht exportieren. Hier feheln in jedem Fall die Konsruktoren, damit auch Deine Implementierung verwendet wird!
    Letzten Endes liegt die Implementierung ja in AddToList und niemand sonst muss sehen wie die interne Klasse aussieht.

    Je nach Design der Lib/DLL musst Du unterscheiden. Wenn DLLs und EXEs immer als paar ausgeliefert werden kann man auch solche Klassen exportieren. SOll die DLL unabhängig laufen dann kann man von solchen Konzepten und Klassen nur abraten. Dann solltest Du auf ein pures Interface System umschwenken.
    Bleibt also die Frage, warum Deine Klasse überhaupt Daten-Member hat und nicht pure Interfaces verwendet.

    Warnungen ausschalten solltest Du in keinem Fall.

    BTW: Pimpl gibt es auch ncoh.



  • Absolute Best Practise wäre es in einem dll Interface nur einfache Funktionen und primitive Datentypen (PODs) zu haben. Das Nächstbeste was du tun kannst wäre es dann wohl wie schon angesprochen nur Interfaces zu verwenden. Das funktioniert mit gewissen Einschränkungen noch ganz gut.



  • Danke schon mal,

    also wenn ich das richtig verstanden habe liegt mein Problem eher am:
    - Wie schreibe ich ein gutes Interface

    Denn dann spar ich mir ja viel export und import-Arbeit.

    Das wird happig bei meinen Strukturen.
    Derzeit ist es so, dass die DLL immer mit der Exe ausgeliefert wird, es soll aber auch DLLs geben die separat laufen. Also fehlt mir einfach noch der "richtige Dreh" für ein Interface.

    Also gut, dann werd ich mir mal ein Buch suchen, wo ich mal etas mehr über Interface und DLL lernen kann.
    Jemand eine Buchempfehlung?

    Viele Grüße,
    Stefan



  • Wenn eine DLL separat ausgeliefert werden soll, dann dürfen in keinem Fall im Header Objekte sein deren Layout sich je Compiler Version ändert...

    Ein gutes DLL Interfcae verwendet z.B. auch nur PODs.
    Klassen exportieren würde ich niermals, wenn es um losgelöste DLLs geht. Das kann immer nur mit einer Compilerversion funktionieren.



  • Hallo,
    dazu noch eine Frage (so rein um es zu verstehen).

    Ich nutze derzeit oftmals wxWidgets. Und meist auch ImageMagick.
    Beides gibt es ja für C++.
    Wenn ich beides als LIB in mein Projekt hole, bekomme ich vom Linker viele viele Fehler über doppelten Code.

    Beide sagen, ich solle als DLL einbinden. Also schnell beides als DLL durch den Compiler durchgelassen und eingebunden: geht! Kein doppelter Code.

    Die nutzen aber jetzt kein Interface. Ich habe Header-Dateien zu DLL und kann somit direkt linken. Ich vermute jetzt, dass liegt daran, weil ich die DLL und Exe in einem Zug mit dem gleichen Compiler gebaut habe. Ich muss allerdings jetzt auch die DLL-Dateien mit ausliefern. Und wenn sich an der DLL was ändert (z.B. neue Version oder Updates) muss ich die Exe auch neu schreiben.

    Wenn ich euch richtig verstanden habe, ist das jetzt aber nicht gerade eine gute Lösung. Bzw nur so lange eine akzeptable Lösung, solang wxWidgets nicht mit ImageMagick versucht zu komunizieren. (Was sicherlich nicht passiert, weil die zwei ja so nix miteinander zu tun haben).

    Sinnvoller wäre es, beide als Lib einzubinden und nur den gemeinsamen Code in eine DLL zu exportieren, welches über ein Interface angesprochen wird.
    (Was sich sicherlich nicht machen lässt ohne wxWidgets und ImageMagick selbst in die Hand zu nehmen und darin rumzubasteln.)

    Wenn ich das jetzt richtig verstanden habe, dann bin ich auch schon friedlich, schließe den Post als "gelöst" und werde meine Programmierer-Struktur-DLL-Lib-Interface-Gedanken für meine eigenen SDKs neu überdenken.

    Vielen Dank,
    Stefan


Anmelden zum Antworten