Avantages des bibliothèques d'en-tête uniquement

98

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é?

Nébuleuse Renard
la source
Principalement des modèles, mais cela rendra également un peu plus facile à distribuer et à utiliser.
BoBTFish
4
Je voudrais ajouter les inconvénients d'une bibliothèque d'en-tête uniquement à la portée de la question ...
moooeeeep
Quels inconvénients n'y a-t-il pas déjà été mentionnés?
NebulaFox
7
@moooeeeep: pour les inconvénients, vous voudrez peut-être lire le paragraphe "Arrêter l'inlining code" dans la page Web de C ++ Chromium Projects.
Mr.C64

Réponses:

57

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, dllou .sofichiers). 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.

Luchian Grigore
la source
6
"Avoir une bibliothèque avec en-tête seulement signifie également que vous n'avez pas à vous soucier des différentes plates-formes où la bibliothèque pourrait être utilisée": seulement si vous n'avez pas à maintenir la bibliothèque. Sinon, c'est un cauchemar, avec des rapports de bogues que vous ne pouvez pas reproduire ou tester sur le matériel dont vous disposez.
James Kanze
1
Je viens de poser une question similaire sur les avantages de performance de l'en-tête uniquement. Comme vous pouvez le voir, il n'y a aucune différence de taille de code. Cependant, l'exemple d'implémentation d'en-tête uniquement a été 7% plus lent. stackoverflow.com/questions/12290639/…
Homer6
@ Homer6 merci de me cingler. Je n'ai jamais vraiment mesuré cela.
Luchian Grigore
1
@LuchianGrigore Je n'ai trouvé personne d'autre qui en avait. C'est pourquoi il a fallu un certain temps pour répondre. Il y a tellement de commentaires spéculatifs sur «l'augmentation de la taille du code» et la «consommation de mémoire». J'ai enfin un aperçu des différences, même si ce n'est qu'un exemple.
Homer6
@ Homer6. Pourquoi n'augmenterait-il pas la taille du code? En supposant que vous créez plusieurs bibliothèques qui utilisent uniquement l'en-tête lib et que votre application utilise toutes ces bibliothèques, vous devrez avoir plusieurs copies au lieu de créer une liaison avec une seule bibliothèque partagée.
pooya13
60

Avantages de la bibliothèque d'en-tête uniquement:

  • Simplifie le processus de construction. Vous n'avez pas besoin de créer la bibliothèque et vous n'avez pas besoin de spécifier la bibliothèque compilée lors de l'étape de liaison de la construction. Si vous avez une bibliothèque compilée, vous voudrez probablement en créer plusieurs versions: une compilée avec le débogage activé, une autre avec l'optimisation activée et peut-être une autre dépourvue de symboles. Et peut-être même plus pour un système multi-plateforme.

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- #includetê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 #includené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.

David Hammen
la source
21
Le dernier point n'a pas vraiment de sens. Toute documentation raisonnable comprendra la déclaration de fonction, les paramètres, les valeurs de retour, etc. et tous les commentaires associés. Si vous devez vous référer au fichier d'en-tête, la documentation a échoué.
Thomas
6
@Thomas - Même avec le meilleur des bibliothèques professionnelles, je me retrouve souvent obligé de lire l'en-tête "fine". En fait, si la soi-disant documentation «fine» est extraite du code et des commentaires, j'aime généralement lire les en-têtes. Le code et les commentaires m'en disent plus que la documentation générée automatiquement.
David Hammen
2
Le dernier point n'est pas valide. Les en-têtes sont déjà remplis de détails d'implémentation dans les membres privés, ce n'est donc pas comme si le fichier cpp cachait tous les détails d'implémentation. De plus, les langages comme C # sont essentiellement des "en-têtes uniquement" par conception, et l'EDI s'occupe de masquer les détails (les "replier")
Mark Lakata
2
@Tomas: D'accord, le dernier point est complètement faux. Vous pouvez facilement garder l'interface et l'implémentation tout aussi séparées avec des bibliothèques d'en-tête uniquement; vous avez simplement l'en-tête d'interface #include les détails d'implémentation. C'est pourquoi les bibliothèques Boost incluent généralement un sous-répertoire (et un espace de noms) appelé detail.
Nemo
4
@Thomas: Je ne suis pas d'accord. Le fichier d'en-tête est généralement le premier endroit où je vais pour la documentation. Si l'en-tête est bien écrit, il n'y a souvent pas besoin de documentation externe.
Joel Cornett du
14

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.

Phil Rosenberg
la source
8

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.)

James Kanze
la source
0

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)?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source