Was sind die Vorteile einer Nur-Header-Bibliothek und warum sollten Sie sie so schreiben, dass die Implementierung nicht in eine separate Datei gestellt wird?
c++
header-only
NebulaFox
quelle
quelle
Antworten:
Es gibt Situationen, in denen eine Nur-Header-Bibliothek die einzige Option ist, beispielsweise beim Umgang mit Vorlagen.
Wenn Sie nur eine Header-Bibliothek haben, müssen Sie sich keine Gedanken über verschiedene Plattformen machen, auf denen die Bibliothek möglicherweise verwendet wird. Wenn Sie die Implementierung trennen, verbergen Sie normalerweise Implementierungsdetails und verteilen die Bibliothek als Kombination aus Headern und Bibliotheken (
lib
,dll
's oder.so
Dateien). Diese müssen natürlich für alle verschiedenen Betriebssysteme / Versionen kompiliert werden, die Sie unterstützen.Sie könnten die Implementierungsdateien auch verteilen, dies würde jedoch einen zusätzlichen Schritt für den Benutzer bedeuten - das Kompilieren Ihrer Bibliothek vor der Verwendung.
Dies gilt natürlich von Fall zu Fall . Beispielsweise nehmen manchmal nur Header-Bibliotheken zu
Codegröße &Kompilierungszeiten.quelle
Vorteile der Nur-Header-Bibliothek:
Nachteile einer Nur-Header-Bibliothek:
Größere Objektdateien. Jede Inline-Methode aus der Bibliothek, die in einer Quelldatei verwendet wird, erhält auch ein schwaches Symbol, eine Outline-Definition in der kompilierten Objektdatei für diese Quelldatei. Dies verlangsamt den Compiler und verlangsamt auch den Linker. Der Compiler muss all das Aufblähen erzeugen, und dann muss der Linker es herausfiltern.
Längere Zusammenstellung. Zusätzlich zu dem oben erwähnten Aufblähungsproblem dauert die Kompilierung länger, da die Header bei einer Nur-Header-Bibliothek von Natur aus größer sind als bei einer kompilierten Bibliothek. Diese großen Header müssen für jede Quelldatei, die die Bibliothek verwendet, analysiert werden. Ein weiterer Faktor ist, dass diese Header-Dateien in einer Nur-Header-Bibliothek sowohl
#include
Header enthalten müssen, die von den Inline-Definitionen benötigt werden, als auch die Header, die benötigt würden, wenn die Bibliothek als kompilierte Bibliothek erstellt worden wäre.Mehr verworrene Zusammenstellung. Sie erhalten viel mehr Abhängigkeiten mit einer Nur-Header-Bibliothek aufgrund der zusätzlichen
#include
s, die mit einer Nur-Header-Bibliothek benötigt werden. Wenn Sie die Implementierung einiger Schlüsselfunktionen in der Bibliothek ändern, müssen Sie möglicherweise das gesamte Projekt neu kompilieren. Nehmen Sie diese Änderung in der Quelldatei für eine kompilierte Bibliothek vor. Sie müssen lediglich diese eine Bibliotheksquelldatei neu kompilieren, die kompilierte Bibliothek mit dieser neuen O-Datei aktualisieren und die Anwendung erneut verknüpfen.Für den Menschen schwerer zu lesen. Selbst mit der besten Dokumentation müssen Benutzer einer Bibliothek häufig die Header für die Bibliothek lesen. Die Header in einer Nur-Header-Bibliothek sind mit Implementierungsdetails gefüllt, die das Verständnis der Schnittstelle beeinträchtigen. Bei einer kompilierten Bibliothek sehen Sie nur die Benutzeroberfläche und einen kurzen Kommentar zur Funktionsweise der Implementierung. Das ist normalerweise alles, was Sie möchten. Das ist wirklich alles was du willst. Sie sollten keine Implementierungsdetails kennen müssen, um zu wissen, wie die Bibliothek verwendet wird.
quelle
detail
.Ich weiß, dass dies ein alter Thread ist, aber niemand hat ABI-Schnittstellen oder bestimmte Compilerprobleme erwähnt. Also dachte ich, ich würde.
Dies basiert im Wesentlichen auf dem Konzept, dass Sie entweder eine Bibliothek mit einem Header schreiben, um sie an andere zu verteilen, oder sich selbst wiederverwenden, anstatt alles in einem Header zu haben. Wenn Sie daran denken, einen Header und Quelldateien wiederzuverwenden und diese in jedem Projekt neu zu kompilieren, trifft dies nicht wirklich zu.
Wenn Sie Ihren C ++ - Code kompilieren und eine Bibliothek mit einem Compiler erstellen, versucht der Benutzer, diese Bibliothek mit einem anderen Compiler oder einer anderen Version desselben Compilers zu verwenden. Aufgrund von Binärinkompatibilität können Linkerfehler oder seltsames Laufzeitverhalten auftreten.
Beispielsweise ändern Compiler-Anbieter häufig ihre Implementierung der STL zwischen den Versionen. Wenn Sie eine Funktion in einer Bibliothek haben, die einen std :: -Vektor akzeptiert, erwartet sie, dass die Bytes in dieser Klasse so angeordnet sind, wie sie beim Kompilieren der Bibliothek angeordnet wurden. Wenn der Anbieter in einer neuen Compilerversion die Effizienz von std :: vector verbessert hat, erkennt der Code des Benutzers die neue Klasse, die möglicherweise eine andere Struktur hat, und übergibt diese neue Struktur an Ihre Bibliothek. Von dort geht es bergab ... Aus diesem Grund wird empfohlen, STL-Objekte nicht über Bibliotheksgrenzen hinweg zu übergeben. Gleiches gilt für CRT-Typen (C Run-Time).
Wenn Sie über die CRT sprechen, müssen Ihre Bibliothek und der Quellcode des Benutzers im Allgemeinen mit derselben CRT verknüpft werden. Wenn Sie in Visual Studio Ihre Bibliothek mit der Multithread-CRT erstellen, der Benutzer jedoch mit der Multithread-Debug-CRT verknüpft, treten Verknüpfungsprobleme auf, da Ihre Bibliothek möglicherweise nicht die benötigten Symbole findet. Ich kann mich nicht erinnern, um welche Funktion es sich handelt, aber für Visual Studio 2015 hat Microsoft eine CRT-Funktion inline erstellt. Plötzlich befand es sich im Header nicht mehr in der CRT-Bibliothek, sodass Bibliotheken, die erwarteten, dass sie zum Zeitpunkt der Verknüpfung gefunden werden, nicht mehr funktionieren konnten, was zu Verbindungsfehlern führte. Das Ergebnis war, dass diese Bibliotheken mit Visual Studio 2015 neu kompiliert werden mussten.
Sie können auch Linkfehler oder seltsames Verhalten erhalten, wenn Sie die Windows-API verwenden, aber mit anderen Unicode-Einstellungen für den Bibliotheksbenutzer erstellen. Dies liegt daran, dass die Windows-API über Funktionen verfügt, die entweder Unicode- oder ASCII-Zeichenfolgen und Makros / Definitionen verwenden, die basierend auf den Unicode-Einstellungen des Projekts automatisch die richtigen Typen verwenden. Wenn Sie eine Zeichenfolge über die Bibliotheksgrenze übergeben, die vom falschen Typ ist, werden die Dinge zur Laufzeit unterbrochen. Oder Sie stellen möglicherweise fest, dass das Programm überhaupt nicht verknüpft ist.
Diese Dinge gelten auch für die Übergabe von Objekten / Typen über Bibliotheksgrenzen von anderen Bibliotheken von Drittanbietern (z. B. einem Eigenvektor oder einer GSL-Matrix). Wenn die Bibliothek eines Drittanbieters ihren Header zwischen dem Kompilieren Ihrer Bibliothek und dem Kompilieren des Codes durch Ihren Benutzer ändert, werden die Dinge kaputt gehen.
Um sicher zu gehen, können Sie Bibliotheksgrenzen nur in Typen und POD (Plain Old Data) erstellen. Idealerweise sollte sich jeder POD in Strukturen befinden, die in Ihren eigenen Headern definiert sind und sich nicht auf Header von Drittanbietern stützen.
Wenn Sie nur eine Header-Bibliothek bereitstellen, wird der gesamte Code mit denselben Compilereinstellungen und mit denselben Headern kompiliert, sodass viele dieser Probleme behoben werden (vorausgesetzt, die Version der dritten teilweise Bibliotheken, die Sie und Ihr Benutzer verwenden, ist API-kompatibel).
Es gibt jedoch oben erwähnte Negative, wie beispielsweise die verlängerte Kompilierungszeit. Möglicherweise führen Sie auch ein Unternehmen, sodass Sie möglicherweise nicht alle Details zur Implementierung Ihres Quellcodes an alle Benutzer weitergeben möchten, falls einer von ihnen diese stiehlt.
quelle
Der Hauptvorteil besteht darin, dass Sie Quellcode bereitstellen müssen, sodass Sie Fehlerberichte auf Computern und Compilern erhalten, von denen Sie noch nie gehört haben. Wenn es sich bei der Bibliothek ausschließlich um Vorlagen handelt, haben Sie nicht viel Auswahl, aber wenn Sie die Wahl haben, ist nur der Header normalerweise eine schlechte technische Wahl. (Auf der anderen Seite bedeutet Header natürlich nur, dass Sie keinen Integrationsvorgang dokumentieren müssen.)
quelle