Probleme mit statischer Bibliothek im Debug-Modus



  • Hallo zusammen
    Ich habe mich mal etwas ausführlicher mit statischen Bibliotheken befasst. Ich habe einige Projekte, von denen ich eine statische Lib erstellt habe (z.B. eine Bruch-Klasse), um sie einfach wiederzuverwenden (ich verwende MSVC++ 2008 Express).

    Nun ist es so, dass ich eine Projektmappe habe, in der die Libs erstellt werden, und dort habe ich z.B. einen Header und eine Implementierungsdatei.

    Ich möchte auch gleich, dass die entsprechende Bibliothek automatisch gelinkt wird, also dass der Anwender nur die Include-/Libverzeichnisse bekannt machen muss und einen Header einbinden kann.
    In den Header des erstellenden Projekts habe ich geschrieben:

    #ifndef CREATING_LIB	// nur beim Benutzen des Headers, nicht bei Erstellung der Bibliothek - Makro in CPP-Datei definiert
     #ifdef _DEBUG
      #pragma message("Automatisches Linken mit Fraction-d.lib (Debug)")
      #pragma comment(lib, "Fraction-d.lib")
     #else
      #pragma message("Automatisches Linken mit Fraction.lib (Release)")
      #pragma comment(lib, "Fraction.lib")
     #endif	// _DEBUG
    #endif	// CREATING_LIB
    

    Das geht auch so weit so gut. Wenn man in einem anderen Projekt die Verzeichnisse bekannt macht und den Header einbindet, kann man auch alle Funktionen benutzen. Doch leider geht das nur im Release-Modus. Sobald ich das Programm im Debug-Modus kompilieren will, bekomme ich folgende Linkerfehler:

    1>Main.cpp
    1>Automatisches Linken mit Fraction-d.lib (Debug)
    1>Verknüpfen...
    1>MSVCRTD.lib(MSVCR90D.dll) : error LNK2005: "public: virtual __thiscall std::exception::~exception(void)" (??1exception@std@@UAE@XZ) ist bereits in LIBCMTD.lib(stdexcpt.obj) definiert.
    1>MSVCRTD.lib(MSVCR90D.dll) : error LNK2005: "public: __thiscall std::exception::exception(void)" (??0exception@std@@QAE@XZ) ist bereits in LIBCMTD.lib(stdexcpt.obj) definiert.
    1>MSVCRTD.lib(MSVCR90D.dll) : error LNK2005: "public: __thiscall std::exception::exception(class std::exception const &)" (??0exception@std@@QAE@ABV01@@Z) ist bereits in LIBCMTD.lib(stdexcpt.obj) definiert.
    1>MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_info@@AAE@ABV0@@Z) ist bereits in LIBCMTD.lib(typinfo.obj) definiert.
    1>MSVCRTD.lib(ti_inst.obj) : error LNK2005: "private: class type_info & __thiscall type_info::operator=(class type_info const &)" (??4type_info@@AAEAAV0@ABV0@@Z) ist bereits in LIBCMTD.lib(typinfo.obj) definiert.
    1>LINK : warning LNK4098: Standardbibliothek "MSVCRTD" steht in Konflikt mit anderen Bibliotheken; /NODEFAULTLIB:Bibliothek verwenden.

    Wenn ich den Rat befolge und "MSVCRTD" ignoriere, läuft das Programm zwar, aber ich bekomme folgende Warnungen:

    1>Fraction-d.lib(Fraction.obj) : warning LNK4217: Lokal definiertes Symbol "??1exception@std@@UAE@XZ (public: virtual __thiscall std::exception::~exception(void))" wurde in __unwindfunclet??0logic_error@std@@QAE@ABV???0logic\_error@std@@QAE@ABV?basic_string@DU?chartraits@D@std@@V?char_traits@D@std@@V?allocator@D@2@@1@@Z0-Funktion importiert. 1>Fraction-d.lib(Fraction.obj) : warning LNK4217: Lokal definiertes Symbol "??0exception@std@@QAE@XZ (public: \_\_thiscall std::exception::exception(void))" wurde in "public: \_\_thiscall std::logic\_error::logic\_error(class std::basic\_string,class std::allocator > const &)" (??0logic\_error@std@@QAE@ABV?basic_string@DU?chartraits@D@std@@V?char_traits@D@std@@V?allocator@D@2@@1@@Z)-Funktion importiert.
    1>Fraction-d.lib(Fraction.obj) : warning LNK4217: Lokal definiertes Symbol "??0exception@std@@QAE@ABV01@@Z (public: __thiscall std::exception::exception(class std::exception const &))" wurde in "public: __thiscall std::logic_error::logic_error(class std::logic_error const &)" (??0logic_error@std@@QAE@ABV01@@Z)-Funktion importiert.
    1>Das Manifest wird eingebettet...

    Nun frage ich mich, ob diese Warnungen auf eine legitime Weise beseitigen kann (d.h. nicht einfach ignorieren) und weshalb sie überhaupt auftreten. Was ist denn am Debug-Modus speziell, so dass gewisse Bibliotheken ignoriert werden müssen?



  • Hallo,

    an z.B. dieser Zeile:

    MSVCRTD.lib(MSVCR90D.dll) : error LNK2005: "public: virtual __thiscall std::exception::~exception(void)" (??1exception@std@@UAE@XZ) ist bereits in LIBCMTD.lib(stdexcpt.obj) definiert.

    erkennt man, dass ein Modul gegen die msvcrtd.lib, ein anderes gegen die LIBCMTD.lib gelinkt wurde, hier wird einmal statisch gelinkt, das andere Mal soll die DLL-Version der Laufzeitbibliothek verwendet werden. Das sollte nicht sein, alle Module sollten die gleiche "Art" der Laufzeitbibliothek verwenden. Also alles so kompilieren, dass auch die Einstellungen für Code-Erzeugung->Laufzeitbibliothek für alle Projektbestandteile (Bibliotheken usw...) übereinstimmen.

    MfG,

    Probe-Nutzer



  • Vielen Dank für deine Hilfe.

    Ich hab jetzt bei dem Projekt, das die Lib erstellt, die CRT statisch eingebunden (/MT bzw. /MTd) und meine Makros folgendermassen angepasst:

    #ifndef CREATING_LIB	// nur beim Benutzen des Headers, nicht bei Erstellung der Bibliothek
     #ifdef _DEBUG
      #pragma message("Automatisches Linken mit Fraction-d.lib (Debug)")
      #pragma comment(linker, "/NODEFAULTLIB:MSVCRTD")
      #pragma comment(lib, "Fraction-d.lib")
     #else
      #pragma message("Automatisches Linken mit Fraction.lib (Release)")
      #pragma comment(linker, "/NODEFAULTLIB:LIBCMT")
      #pragma comment(lib, "Fraction.lib")
     #endif	// _DEBUG
    #endif	// CREATING_LIB
    

    Ist das okay, diese Bibliotheken zu ignorieren? Momentan geht es beim Anwendungsprojekt mit allen Konfigurationen (Release Static, Debug Static, Release DLL, Debug DLL), aber kann man so auf Probleme stossen?

    Wie ist das überhaupt, wenn in der Lib die C-Runtime statisch eingebunden wird und im Anwendungsprojekt dynamisch? Was passiert da schlussendlich?



  • Nur mal so.
    Ich benutze die #pragma Anweisungen nur sehr ungerne. Ich finde das wesentlich komfortabler in den Dialogen einzustellen. Ich passe in einem Projekt bei Portierung lieber mal den Dialog an, als den Code anzufassen.



  • Ja, das habe ich mir auch überlegt. Jedoch wollte ich eigentlich so wenig wie möglich dem Anwender überlassen, sodass es wie bei der Standardbibliothek reicht, einen Header zu inkludieren.

    Was denkst du, wie viele Linkerfehler vermieden werden können, wenn man nicht noch selber Debug/Release und Static-Lib/DLL bei jedem Projekt aufeinander abstimmen muss? Und reden wir jetzt mal nicht von den in Konflikt geratenen Bibliotheken, die man auch noch ignorieren müsste...



  • Nexus schrieb:

    Ist das okay, diese Bibliotheken zu ignorieren? Momentan geht es beim Anwendungsprojekt mit allen Konfigurationen (Release Static, Debug Static, Release DLL, Debug DLL), aber kann man so auf Probleme stossen?

    Nun ja, du müsstest noch weitere Bibliotheken ignorieren, denn der (evtl. sehr unbedarfte) Anwender kann noch andere Konfigurationen eingestellt haben, eine Liste hier:

    http://msdn.microsoft.com/en-us/library/6wtdswk0(VS.80).aspx

    Ich halte von "Ignorieren" nicht sehr viel, das sollte nur letztes Mittel sein, um etwas lauffähiges zu erzeugen.

    Nexus schrieb:

    Wie ist das überhaupt, wenn in der Lib die C-Runtime statisch eingebunden wird und im Anwendungsprojekt dynamisch? Was passiert da schlussendlich?

    Da kann es mehrere Probleme geben, eine kleine (unvollständige) Liste unter "MORE INFORMATION", hier:

    http://support.microsoft.com/kb/140584/en-us

    Da wird auch gezeigt, wie man noch weitere Makros einsetzen kann, damit andere es leichter haben, deine Bibliothek einzusetzen, denkbar wäre mit diesen Makros dann auch, gleich die "richtige" Bibliothek mit #pragma zu setzen, je nach Linkeroption.

    MfG,

    Probe-Nutzer



  • Okay, vielen Dank für die Links.

    Jedoch stimmt das bei der MSDN nicht ganz; ich erhalte immer noch Linkerfehler, wenn ich die Bibliotheken so wie vorgeschrieben ignoriere.

    Und ignorieren ist vielleicht schon nicht ideal, aber gibt es eine bessere Möglichkeit?

    Und heisst das, ich müsste sogar vier statische Bibliotheken erstellen, um eine konsistente Nutzung der CRT zu ermöglichen? Das fände ich langsam ein bisschen übertrieben, zumal ich nur eine kleine Klasse bereitstellen möchte...

    Edit: Ich habs jetzt mal vorläufig so gemacht, das geht mit allen Konfigurationen im Anwendungsprojekt. Die Lib selber linkt statisch mit der CRT, führt das zu oben erwähnten Problemen?

    Also so sehen die sehr übersichtlichen Pragma-Direktiven aus:

    #ifndef CREATING_LIB
     #ifdef _DEBUG
      #ifdef _DLL
       #pragma comment(linker, "/NODEFAULTLIB:LIBCMTD")
      #else
       #pragma comment(linker, "/NODEFAULTLIB:MSVCRTD")
      #endif // _DLL
      #pragma message("Automatisches Linken mit Fraction-d.lib (Debug)")
      #pragma comment(lib, "Fraction-d.lib")
     #else
      #ifdef _DLL
       #pragma comment(linker, "/NODEFAULTLIB:LIBCMT")
      #else
       #pragma comment(linker, "/NODEFAULTLIB:MSVCRT")
      #endif // _DLL
      #pragma message("Automatisches Linken mit Fraction.lib (Release)")
      #pragma comment(lib, "Fraction.lib")
     #endif	// _DEBUG
    #endif	// CREATING_LIB
    

    Aber ich müsste noch mehr Bibliotheken ignorieren... Naja so läufts zumindest bei mir 😉



  • Dieser Thread wurde von Moderator/in HumeSikkins aus dem Forum C++ 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.



  • Aber wie macht man das am besten, wenn man eine statische Bibliothek zur Verfügung stellen will? Kommt man da nicht drum herum, alle 4 möglichen Kombinationen von Konfigurationen (Debug/Release - CRT statisch/dynamisch gelinkt) einzurichten?

    Oder reicht es, nur auf Debug/Release Rücksicht zu nehmen und z.B. gegen statisch gegen die CRT zu linken?



  • Mach doch einfach verschiedene ProjectDateien.
    So habe ich es bis jetzt immer gehandhabt und hatte keine Probleme damit.



  • Welche Dateien meinst du?

    Momentan stelle ich zwei Bibliotheken (.lib) pro Projekt zur Verfügung, also für Debug- und Releasemodus. Soll ich total 4 pro Projekt erzeugen, um auch noch die CRT-Linkage zu berücksichtigen?



  • 1. Das ignorieren der Default Libs ist meistens nicht das Problem.
    Diese Meldungen passieren fast immer nur, wenn es zwei Objekt Module gibt, die mit unterschiedlichen CRT Enstellungen kompiliert wurden zusammen gelinkt werden.
    Es ist nie gut NODEFAULTLIB zu verwenden. Korrekt ist es mit /VERRBOSE das schuldige Objekt Modul zu finden.
    2. Ich halte pragmas comment lib immer für den besseren Weg.



  • Okay, momentan habe ich es so gelöst, dass beim Linken mit der falschen CRT-Version über #error ein Fehler ausgelöst wird. Doch so wird der Anwender gezwungen, dynamisch gegen die CRT zu linken.

    Jetzt habe ich 2 statische Bibliotheken pro Projekt (Debug/Release). Wenn ich eine statische Linkage gegen die CRT auch ermöglichen will, komme ich nicht darum herum, total 4 Libs zu erstellen?



  • Nexus schrieb:

    Jetzt habe ich 2 statische Bibliotheken pro Projekt (Debug/Release). Wenn ich eine statische Linkage gegen die CRT auch ermöglichen will, komme ich nicht darum herum, total 4 Libs zu erstellen?

    Kann man dann eigentlich so eine Header-Datei machen:

    #ifdef _DEBUG
    #    ifdef _DLL
    #       pragma comment( lib, "bla_debug_dynamic_crt.lib" )
    #    else
    #       pragma comment( lib, "bla_debug_static_crt.lib" )
    #    endif
    #else
    #    ifdef _DLL
    #       pragma comment( lib, "bla_release_dynamic_crt.lib" )
    #    else
    #       pragma comment( lib, "bla_release_static_crt.lib" )
    #    endif
    #endif
    

    ? Oder wie wird das normalerweise gemacht, manuell die Bibliothek angeben lassen? Gibt's bei so 'ner Header-Datei Nachteile?

    edit: Das hier ginge auch 🙂

    #ifdef _DEBUG
    #    define LIBINC_DBG_REL "debug"
    #else
    #    define LIBINC_DBG_REL "release"
    #endif
    
    #ifdef _DLL
    #    define LIBINC_STAT_DYN "dynamic"
    #else
    #    define LIBINC_STAT_DYN "static"
    #endif
    
    #pragma comment( lib, "bla_" LIBINC_DBG_REL "_" LIBINC_STAT_DYN ".lib" )
    #undef LIBINC_DBG_REL
    #undef LIBINC_STAT_DYN
    


  • Ja, momentan hab ichs aber eben auf dynamische CRT-Linkage eingeschränkt, um zwei verschiedene Konfigurationen zu vermeiden:

    #ifndef _DLL
     #error Muss dynamisch gegen die CRT gelinkt werden.
    #endif
    
    #ifdef _DEBUG
     #pragma message("Automatisches Linken mit Fraction-d.lib (Debug)")
     #pragma comment(lib, "Fraction-d.lib")
    #else
     #pragma message("Automatisches Linken mit Fraction.lib (Release)")
     #pragma comment(lib, "Fraction.lib")
    #endif
    

    Also besteht die einzige Möglichkeit doch darin, vier statische Bibliotheken bereitzustellen?



  • Sorry, wenn ich die Frage zum fünften Mal stelle 😉
    Gibt es keine andere Möglichkeit, als 4 statische Bibliotheken (.lib) zu erstellen, wenn man sowohl dynamische als auch statische CRT-Linkage und auch Debug- und Release-Modus ermöglichen will?



  • Nexus schrieb:

    Gibt es keine andere Möglichkeit, als 4 statische Bibliotheken (.lib) zu erstellen, wenn man sowohl dynamische als auch statische CRT-Linkage und auch Debug- und Release-Modus ermöglichen will?

    Ich bin mir zumindest relativ sicher, dass es nicht anders geht. Nicht zu vergessen, diese 4 Libs übrigens ja nur pro Compilerversion 😃



  • Badestrand schrieb:

    Nicht zu vergessen, diese 4 Libs übrigens ja nur pro Compilerversion 😃

    Und ich dachte, statische Bibliotheken wären ein guter Weg zur Wiederverwendung von Code... 🙄

    Wahrscheinlich werde ich den Benutzer einfach zwingen, MSVC++ mit dynamischer CRT-Linkage zu verwenden... 😃

    Aber wie ist das bei anderen Bibliotheken, z.B. Boost, gelöst? Gibt es da für alle existierenden Compiler eigene Libs?



  • Nein! Es gibt IMHO keine andere Möglichkeit.
    Statisch/Dynamisch, Debug/Non-Debug macht leider 2x2=4
    Und das je Compiler Version...



  • Okay, danke für die Antworten.
    Dann werd ich mir das halt wohl oder übel antun müssen. 😞

    Naja, so viel zu Code-Wiederverwendung... Irgendwie beschleicht mich das Gefühl, eine eigene CPP-Datei statt einer Lib mitzuliefern wäre einfacher... 🙄


Anmelden zum Antworten