Quels sont les avantages d'une bibliothèque d'en-tête uniquement et pourquoi l'écririez-vous de cette façon contre la mise en œuvre dans un fichier séparé?
Quels sont les avantages d'une bibliothèque d'en-tête uniquement et pourquoi l'écririez-vous de cette façon contre la mise en œuvre dans un fichier séparé?
Réponses:
Il y a des situations où une bibliothèque avec en-tête uniquement est la seule option, par exemple lorsqu'il s'agit de modèles.
Avoir une bibliothèque avec en-tête uniquement signifie également que vous n'avez pas à vous soucier des différentes plates-formes où la bibliothèque pourrait être utilisée. Lorsque vous séparez l'implémentation, vous le faites généralement pour masquer les détails de l'implémentation et distribuer la bibliothèque sous la forme d'une combinaison d'en-têtes et de bibliothèques ( lib
, dll
ou .so
fichiers). Celles-ci doivent bien sûr être compilées pour tous les différents systèmes d'exploitation / versions que vous proposez.
Vous pouvez également distribuer les fichiers d'implémentation, mais cela signifierait une étape supplémentaire pour l'utilisateur - compiler votre bibliothèque avant de l'utiliser.
Bien entendu, cela s'applique au cas par cas . Par exemple, les bibliothèques d'en-tête uniquement augmentent parfoistaille du code & temps de compilation.
Avantages de la bibliothèque d'en-tête uniquement:
Inconvénients d'une bibliothèque d'en-tête uniquement:
Fichiers objets plus volumineux. Chaque méthode en ligne de la bibliothèque qui est utilisée dans un fichier source aura également un symbole faible, une définition hors ligne dans le fichier objet compilé pour ce fichier source. Cela ralentit le compilateur et ralentit également l'éditeur de liens. Le compilateur doit générer tout ce gonflement, puis l'éditeur de liens doit le filtrer.
Compilation plus longue. En plus du problème de ballonnement mentionné ci-dessus, la compilation prendra plus de temps car les en-têtes sont intrinsèquement plus grands avec une bibliothèque d'en-tête uniquement qu'une bibliothèque compilée. Ces gros en-têtes devront être analysés pour chaque fichier source qui utilise la bibliothèque. Un autre facteur est que ces fichiers d'en-tête dans une bibliothèque d'en-tête uniquement ont les en- #include
têtes requis par les définitions en ligne ainsi que les en-têtes qui seraient nécessaires si la bibliothèque avait été construite en tant que bibliothèque compilée.
Une compilation plus enchevêtrée. Vous obtenez beaucoup plus de dépendances avec une bibliothèque d'en-tête uniquement en raison de ces éléments supplémentaires #include
nécessaires avec une bibliothèque d'en-tête uniquement. Modifiez l'implémentation d'une fonction clé dans la bibliothèque et vous devrez peut-être recompiler l'ensemble du projet. Apportez cette modification dans le fichier source d'une bibliothèque compilée et tout ce que vous avez à faire est de recompiler ce fichier source de bibliothèque, de mettre à jour la bibliothèque compilée avec ce nouveau fichier .o et de relier l'application.
Plus difficile à lire pour l'humain. Même avec la meilleure documentation, les utilisateurs d'une bibliothèque doivent souvent recourir à la lecture des en-têtes de la bibliothèque. Les en-têtes d'une bibliothèque d'en-tête uniquement sont remplis de détails d'implémentation qui empêchent de comprendre l'interface. Avec une bibliothèque compilée, tout ce que vous voyez est l'interface et un bref commentaire sur ce que fait l'implémentation, et c'est généralement tout ce que vous voulez. C'est vraiment tout ce que vous devriez souhaiter. Vous ne devriez pas avoir à connaître les détails d'implémentation pour savoir comment utiliser la bibliothèque.
detail
.
Je sais que c'est un ancien thread, mais personne n'a mentionné les interfaces ABI ou les problèmes spécifiques du compilateur. Alors j'ai pensé que je le ferais.
Ceci est fondamentalement basé sur le concept selon lequel vous écrivez une bibliothèque avec un en-tête à distribuer aux gens ou vous réutilisez vous-même plutôt que d'avoir tout dans un en-tête. Si vous envisagez de réutiliser un en-tête et des fichiers source et de les recompiler dans chaque projet, cela ne s'applique pas vraiment.
Fondamentalement, si vous compilez votre code C ++ et créez une bibliothèque avec un compilateur, l'utilisateur essaie d'utiliser cette bibliothèque avec un compilateur différent ou une version différente du même compilateur, vous pouvez obtenir des erreurs de l'éditeur de liens ou un comportement d'exécution étrange en raison d'une incompatibilité binaire.
Par exemple, les fournisseurs de compilateurs modifient souvent leur implémentation de la STL entre les versions. Si vous avez une fonction dans une bibliothèque qui accepte un std :: vector, alors elle s'attend à ce que les octets de cette classe soient arrangés de la manière dont ils étaient arrangés lorsque la bibliothèque a été compilée. Si, dans une nouvelle version du compilateur, le fournisseur a apporté des améliorations d'efficacité à std :: vector, le code de l'utilisateur voit la nouvelle classe qui peut avoir une structure différente et la transmet à votre bibliothèque. Tout se dégrade à partir de là ... C'est pourquoi il est recommandé de ne pas faire passer les objets STL au-delà des limites de la bibliothèque. Il en va de même pour les types C Run-Time (CRT).
Lorsque vous parlez du CRT, votre bibliothèque et le code source de l'utilisateur doivent généralement être liés au même CRT. Avec Visual Studio, si vous créez votre bibliothèque à l'aide du CRT multithread, mais que l'utilisateur établit des liens avec le CRT de débogage multithread, vous rencontrerez des problèmes de liaison car votre bibliothèque ne trouvera peut-être pas les symboles dont elle a besoin. Je ne me souviens plus de quelle fonction il s'agissait, mais pour Visual Studio 2015, Microsoft a créé une fonction CRT en ligne. Soudain, c'était dans l'en-tête et non dans la bibliothèque CRT, donc les bibliothèques qui s'attendaient à le trouver au moment de la liaison ne pouvaient plus le faire et cela a généré des erreurs de lien. Le résultat était que ces bibliothèques devaient être recompilées avec Visual Studio 2015.
Vous pouvez également obtenir des erreurs de lien ou un comportement étrange si vous utilisez l'API Windows mais que vous créez avec des paramètres Unicode différents pour l'utilisateur de la bibliothèque. Cela est dû au fait que l'API Windows a des fonctions qui utilisent des chaînes Unicode ou ASCII et des macros / définit qui utilisent automatiquement les types corrects en fonction des paramètres Unicode du projet. Si vous passez une chaîne à travers la limite de la bibliothèque qui est du type incorrect, les choses se cassent au moment de l'exécution. Ou vous pouvez constater que le programme n'est pas lié en premier lieu.
Ces choses sont également vraies pour le passage d'objets / types à travers les limites de bibliothèques à partir d'autres bibliothèques tierces (par exemple un vecteur Eigen ou une matrice GSL). Si la bibliothèque tierce modifie son en-tête entre la compilation de votre bibliothèque et la compilation de son code par l'utilisateur, les choses vont casser.
Fondamentalement, pour être sûr, les seules choses que vous pouvez passer à travers les limites de la bibliothèque sont des types intégrés et des POD (Plain Old Data). Idéalement, tout POD doit être dans des structures qui sont définies dans vos propres en-têtes et ne reposent sur aucun en-tête tiers.
Si vous fournissez une bibliothèque d'en-tête uniquement, tout le code est compilé avec les mêmes paramètres de compilateur et avec les mêmes en-têtes, de sorte que beaucoup de ces problèmes disparaissent (à condition que la version de la troisième bibliothèque partielle que vous et votre utilisateur utilise soit compatible avec l'API).
Cependant, certains points négatifs ont été mentionnés ci-dessus, tels que l'augmentation du temps de compilation. Vous pouvez également diriger une entreprise et vous ne voudrez peut-être pas donner tous les détails de l'implémentation de votre code source à tous vos utilisateurs au cas où l'un d'eux le vole.
Le principal "avantage" est qu'il vous oblige à fournir du code source, vous vous retrouverez donc avec des rapports d'erreur sur les machines et avec des compilateurs dont vous n'avez jamais entendu parler. Lorsque la bibliothèque est entièrement constituée de modèles, vous n'avez pas beaucoup de choix, mais lorsque vous avez le choix, l'en-tête seul est généralement un mauvais choix d'ingénierie. (D'un autre côté, bien sûr, l'en-tête signifie uniquement que vous n'avez à documenter aucune procédure d'intégration.)
L'inlining peut être effectué par Link Time Optimization (LTO)
Je voudrais souligner cela car cela diminue la valeur de l'un des deux principaux avantages des bibliothèques d'en-tête uniquement: "vous avez besoin de définitions sur un en-tête à insérer".
Un exemple concret minimal de ceci est montré à: Optimisation du temps de liaison et en ligne
Donc, il vous suffit de passer un drapeau, et l'inlining peut être fait entre les fichiers objets sans aucun travail de refactorisation, plus besoin de conserver les définitions dans les en-têtes pour cela.
Cependant, LTO peut aussi avoir ses propres inconvénients: y a-t-il une raison pour laquelle ne pas utiliser l'optimisation du temps de liaison (LTO)?