Dumme Frage: Lib die von zwei weiteren. libs abhängt, includes richtig setzen



  • Um das mal zu verdeutlichen:
    Nehmen wir an, du hast in der main einen Aufruf der Funktion libA() und im Sourcecode von liba.lib einen Aufruf der Funktion von ABC::foo. Die Klasse ABC steckt in abc.cpp, main in main.cpp. Compiler und Linker machen dann folgendes:

    Beim erstellen von liba.lib:
    - er compiliert die ganzen libA*.cpp's zu libA*.obj's.
    - der Linker baut alles zu der .lib zusammen. Dabei werden die Aufrufe von Symbolen, die in den libA*.obj's nicht zu finden sind, als symbolische Aufrufe stehen gelassen (z.B. ABC::foo)

    Beim Erstellen der .exe:
    - compilieren der abc.cpp und main.cpp zu abc.obj und main.obj
    - Linken von abc.obj, main.obj und liba.lib, hierbei findet der Linker den Aufruf von liba() in main.obj und liba() selbst in liba.lib -> wird zusammengelötet. Der Aufruf von ABC::foo steht ja noch so in liba.lib, der Linker findet in abc.obj den Code dazu -> wird auch verknüpft.

    Ist vermutlich jetzt nicht technisch einwandfrei beschrieben, aber es bringt die idee denke ich rüber.



  • Bist Du sicher?
    Ich hätte glatt behauptet, dass der Linker LibA nicht erstellt, wenn ein Aufruf von ABC::foo() drin ist, ABC::foo() (in ABC.obj) aber nicht mitgelinkt wird?!


  • Mod

    Belli schrieb:

    Bist Du sicher?
    Ich hätte glatt behauptet, dass der Linker LibA nicht erstellt, wenn ein Aufruf von ABC::foo() drin ist, ABC::foo() (in ABC.obj) aber nicht mitgelinkt wird?!

    Probiers aus.



  • Sagen wir ich habe 2 .cpp Dateien: A.cpp und B.cpp und in A.cpp eine Funktion functionA(), die functionB() aus B.cpp aufruft:

    // A.cpp
    #include "B.h"
    
    void functionA() {
       functionB();
    }
    

    Wenn ich diese 2 Dateien jetzt zu einer lib foo.lib kompiliere, dann werden doch gar keine konkreten Adressen eingefuegt, oder? In Pseudosytnax sieht die erstellte Lib dann doch so aus:

    B.o:
    functionB() { ... }
    
    A.o:
    functionA() {
       call [Todo:InsertAdressOfFunctionBhere]
    }
    

    Dh die konkrete Adresse von functionB wurde doch noch NICHT eingefuegt in A.o, oder?

    Das wird dann erst gemacht, wenn ich die foo.lib in einem Projekt einbinde, das zu einer .exe gebaut wird, oder?


  • Mod

    Ja!

    Wie oft muss man diese Frage in diesem Thread noch beantworten? Probiert es doch aus, wenn ihr es nicht glaubt!



  • SeppJ schrieb:

    Ja!

    Wie oft muss man diese Frage in diesem Thread noch beantworten? Probiert es doch aus, wenn ihr es nicht glaubt!

    Wie soll man das ausprobieren? Die.o dissemblieren und versuchen den asm Code zu verstehen?



  • SeppJ schrieb:

    Ja!

    Wie oft muss man diese Frage in diesem Thread noch beantworten? Probiert es doch aus, wenn ihr es nicht glaubt!

    Moment. In pumuckls Posting klang das so, als wuerden nur die Adresse NICHT aufgeloest werden, wenn er das Zeug nicht findet. In meinem Beispiel findet er aber die B.o, also koennte er beim Linken in functionA() doch gleich die echte Adresse von functionB() eintragen?



  • SeppJ schrieb:

    Belli schrieb:

    Bist Du sicher?
    Ich hätte glatt behauptet, dass der Linker LibA nicht erstellt, wenn ein Aufruf von ABC::foo() drin ist, ABC::foo() (in ABC.obj) aber nicht mitgelinkt wird?!

    Probiers aus.

    Hab ich gemacht. Es ist so, wie Du und pumuckl gesagt haben:
    Wenn ich meine Lib bilde, braucht ABC.cpp nicht mitgelinkt zu werden, muss dann aber beim Erstellen der exe-Datei vorhanden sein (bzw. die ABC.obj).
    Wenn ich ABC.cpp schon in mein Lib-Projekt hineinpacke, benötige ich später beim Erstellen einer exe - Datei nur noch die Lib.lib.



  • Moment. In pumuckls Posting klang das so, als wuerden nur die Adresse NICHT aufgeloest werden, wenn er das Zeug nicht findet. In meinem Beispiel findet er aber die B.o, also koennte er beim Linken in functionA() doch gleich die echte Adresse von functionB() eintragen?

    Definiere finden !

    Normal sollte er in Project A (also das project fuer A.lib) nur die Definition (Header) der funktion finden, nicht die Implementation (die source).

    Fügst Du in Project A, die source zu (B.cpp oder so, mit als Quelle) klar dan compiliert er den code mit in die A.Lib.
    Aber spaetestens, wenn in einem Project A.lib und B.lib gleichzeitig verendest (linkst) schmeisst dir das der Linker um die Ohren (double Symbols).
    Aehnlich wenn die Implementation im header steht und nicht inlined wird ...

    Wenn Du eine Lib baust, linkt der Linker ned wirklich, sondern fasst nur die Object-Dateien zu einem Archiv(.a) zusammen.
    Erst der Linker wenn du ein binary(exe/dll binary/so) baut, ersetzt er die symbole mit den Richtigen Adressen der Implementation ....
    Und erst dann mault er dann die typischen Symbolfehler an:
    double symbols und unresolved symbols !

    Fazit:
    Deine ABC.h darf jede Lib kennen (im include Path verfuegbar) und verwenden.
    Implementiert sollte sie nur in einer Lib sein (nur eine lib sollt ABC.cpp in den Sourcen haben).
    Nebeneffekt ist, alle libs die ABC verwenden, erzwingen dann das auch die lib mit zugelinkt wird, die ABC.cpp enthaelt, sonst gibts unresolved symbols ...

    Warum man libs trennt, wenn man die nur zusammen verwenden kann ?
    macht man normal nich ^^
    Aber man koennte gestaffelte Abhanegigkeiten haben. LibB verwendet LibA.
    LibB muss mit LibA immer zusammengelinkt werden.
    Aber libA kann auch ohne LibB verwendet werden ...
    Dann wuerde das trennen Sinn machen ...

    Ciao ...



  • [quote="RHBaum"]

    Fügst Du in Project A, die source zu (B.cpp oder so, mit als Quelle) klar dan compiliert er den code mit in die A.Lib.
    Aber spaetestens, wenn in einem Project A.lib und B.lib gleichzeitig verendest (linkst) schmeisst dir das der Linker um die Ohren (double Symbols).

    Dazu hab ich noch mal folgendes gemacht:
    a.cpp:

    #include <iostream>
    
    class A
    {
    	public:
    		void melde() const;
    };
    
    void A::melde() const
    {
    	std::cout << "bin da\n";
    }
    

    mylib.cpp:

    class A
    {
    	public:
    		void melde() const;
    };
    
    int sum(int a, int b)
    {
    	A x;
    	x.melde();
    
    	return a + b;
    }
    

    test.cpp:

    int sum(int a, int b);
    
    int main()
    {
    	sum(3, 5);
    }
    

    zuerst die lib mit a.cpp gebildet, dann ließ sich test.exe mit
    cl /EHsc test.cpp mylib.lib erstellen.
    Aber auch
    cl /EHsc test.cpp a.cpp mylib.lib
    führte zum Ergebnis ...
    die doppelten Symbole scheinen nicht zu stören, so dass es aussieht, als ob es eine gute Wahl wäre, a.cpp direkt mit in die lib zu nehmen, damit die lib alleine benutzbar ist.


  • Mod

    Und dann macht liba ein kritisches Sicherheitsupdate und jeder der die mylib benutzt, muss auf ein Update von dir warten oder die mylib selber neu bauen, anstatt einfach wie üblich die Laufzeitbibliothek der liba auszutauschen.



  • Das verstehe ich nicht ...
    es macht doch keinen Unterschied, ob ich zur Benutzung von mylib.lib die Dateien mylib.lib und a.obj liefern muss, oder a.obj schon in mylib.lib eingebaut habe und dann nur mylib.lib liefern muss?
    Es scheint mir einfacher zu handlen, wenn alles benötigte in mylib.lib drin ist (vorausgesetzt natürlich, es gibt nicht doch Linkerprobleme, wenn a.obj noch in einer anderen .lib ist, glaube ich aber nach meinem og Test nicht).

    Edit:
    Ich mach nun nicht ständig mit irgendwelchen *.lib s rum, außer denen der WinAPI, aber es ist mir noch nicht untergekommen, dass ich zusätzlich zu einer *.lib noch *.obj Dateien mitlinken muss, damit die lib überhaupt benutzbar ist.
    Dabei muss es ja nicht mal um eine Klasse gehen, für einfache Funktionsimplementierungen in eigenen *.cpp s würden doch die gleichen Regeln gelten, also müsste man ständig über so was stolpern?


  • Mod

    Belli schrieb:

    Dabei muss es ja nicht mal um eine Klasse gehen, für einfache Funktionsimplementierungen in eigenen *.cpp s würden doch die gleichen Regeln gelten, also müsste man ständig über so was stolpern?

    Eine Bibliothek ist nichts anderes als der Objektcode den du aus den cpp erstellst in eine einzige Datei gestopft. Ich spekuliere mal, daher kommt auch der Name.

    Das verstehe ich nicht ...

    Ich meine: Wenn du fremde Bibliotheken mit in deine stopfst, dann musst du auf einmal deren Support mitleisten.



  • SeppJ schrieb:

    Belli schrieb:

    Das verstehe ich nicht ...

    Ich meine: Wenn du fremde Bibliotheken mit in deine stopfst, dann musst du auf einmal deren Support mitleisten.

    Ahh ... jetzt ja ...
    Wenn ich aber ein paar Klassen schreibe, die ich in meiner lib benutze, dann ist es sinnvoll, diese gleich zu integrieren.
    Wenn ich fremde Klassen/Libs benutze, dann kann sich der Benutzer meiner lib diese bei Bedarf selbst zusammensuchen.
    Hab ichs nun?



  • Belli schrieb:

    Wenn ich fremde Klassen/Libs benutze, dann kann sich der Benutzer meiner lib diese bei Bedarf selbst zusammensuchen.

    Dann lieferst du die Lib, die du benutzt mit deiner Lib mit (in der Version, gegen die du implementiert hast). Der Benutzer kann dann nach eigenem Gutdünken und auf eigene Gefahr neuere Versionen dieser 3rd-party Lib an Stelle der von dir mitgelieferten einsetzen.



  • pumuckl schrieb:

    Belli schrieb:

    Wenn ich fremde Klassen/Libs benutze, dann kann sich der Benutzer meiner lib diese bei Bedarf selbst zusammensuchen.

    Dann lieferst du die Lib, die du benutzt mit deiner Lib mit (in der Version, gegen die du implementiert hast). Der Benutzer kann dann nach eigenem Gutdünken und auf eigene Gefahr neuere Versionen dieser 3rd-party Lib an Stelle der von dir mitgelieferten einsetzen.

    Dafür gibts verschiedenste Gründe. Stell dir vor, ich benutze in meiner Lib (pumu.lib) boost.Serialization. Die Lib ist gegen eine relativ alte Version, sagen wir v1.32, implementiert worden und funktioniert wunderbar damit zusammen.
    Du willst pumu.lib in deinem Projekt benutzen, außerdem willst du für einige deiner Klassen auch Boost.Serialization benutzen, und zwar einige Features, die erst in version 1.45 dazugekommen sind. Wenn ich jetzt Boost.Serialization in pumu.lib mit reingelinkt habe, hast du keine Chance, weil die Definitionen aus Boost.Serialization v1.32 in pumu.lib UND in boost_serialization_1_45.lib vorhanden sind. Der Linker spuckt Zähne und du kannst dir aussuchen, auf welche von beiden Libs du verzichten willst.

    Wenn ich die Boost-Lib nicht mit reinlinke, kannst du pumu.lib, boost_serialization_1_45.lib und den von mir mit der Lib mitgelieferten Testcode zusammenlinken und schauen, ob pumu.lib auch mit der neuen Serialization version funktioniert - wegen backward-compatibility der Boost-Bobliotheken sollte das in den meisten Fällen möglich sein.



  • Ja, ich habs ja, obwohl die doppelten Definitionen sind wohl kein Problem, zumindest nicht, soweit sie identisch sind, das hab ich nochmal ausprobiert, indem ich mylib.lib nach b.lib kopiert habe, und test.obj mit mylib.lib und b.lib gelinkt habe.
    Aber vom Prinzip her habe ichs nun kapiert - ich hatte halt irgendwie verdrängt, dass eine statische lib nichts anderes ist, als eine Sammlung von obj s ...



  • Vielen Dank Euch allen!

    Ich denke das ist jetzt geklärt 😃


Anmelden zum Antworten