Les bibliothèques "header-only" en C++
by Baduit
C’est quoi ?
Une bibliothèque “header-only” est une bibliothèque dont tout le code source repose uniquement dans des fichiers headers (fichiers en-tête, souvent avec les extensions .h, .hpp voire .hh). Elles sont souvent aussi partagées en un seul fichier contenant tout le code : dans ce cas-là, la bibliothèque est aussi appelée “single-file”. Il y a quelques exemples assez connus de bibliothèques “header-only” comme par exemple nlohmann json. D’autres bibliothèques font le choix d’être également disponibles sous forme d’une bibliothèque “header-only” tel que {fmt}.
Les raisons pour lesquels ce type de bibliothèque existe sont les suivantes :
- Quand le code utilise beaucoup de templates l’essentiel du code voire la totalité se retrouve dans les headers et donc elle est naturellement composée seulement de headers.
- Le fait que ça soit extrêmement facile à inclure dans son projet. Il n’y a pas de gestionnaire de paquet standardisé comme il existe dans la plupart des autres langages (npm pour javascript, cargo pour rust …). Même s’il en existe certains assez populaire comme vcpkg et conan ils ne font pas l’unanimité et cela fait que de partager et utiliser des dépendances peut ne pas être trivial. Mais si le code ne contient que des headers il suffit de les copier dans son projet et cela marche directement, sans avoir à modifier la manière dont le projet est compilé et c’est en grande partie cette simplicité qui en a fait son succès.
Les avantages
- Facile à inclure dans un projet.
- Facile à partager
Les inconvénients
- Les temps de compilation : à chaque modification, il faut tout recompiler et cela peut prendre du temps. A noter que si le code utilise énormement de templates, faire une bibliothèque “classique” n’aura que peu de gains sur les temps de compilation.
- Les mêmes désavantages que le link statique par rapport au link dynamique d’une bibliothèque. Ce lien en parle un peu plus en détails.
Les mythes
Les bibliothèques “header-only” cassent l’encapsulation car l’utilisateur a accès à toutes les déclarations et définitions de la bibliothèque.
Dans un sens, c’est vrai. Mais il suffit de mettre tout ce qui n’est pas prévu d’être accessible par l’utilisateur dans des namespaces appropriés, comme par exemple un namespace nommé details ou impl. De cette manière, ils ne seront pas directement visibles par l’utilisateur.
Les définitions de macros qui sont nécessaire à l’implémentation mais qu’on ne veut pas exposer fuitent.
Il suffit d’indéfinir les macros que vous ne souhaitez pas exposer à l’utilisateur avec #undef MY_MACRO
.
Il est impossible de gérer les dépendances circulaires.
Tout écrire dans un header n’empêche pas de séparer la déclaration de la définition, comme on le ferait dans un fichier source. La bibliothèque cpp-httplib, par exemple, le fait et a même un script Python pour pouvoir séparer le code en 2 fichiers distincts : un fichier header et un fichier source.
Toutes ces fonctions “inline” vont impacter la rapidité du programme
Le mot-clef inline ne force pas le compilateur à réellement “inline” la fonction. De plus, le fait de rendre “inline” une fonction peut, selon les cas, être positif ou négatif quant aux performances du programme. Sans faire de benchmark, on ne pas être catégorique sur l’effet de cette optimisation.
Devrais-je rendre ma bibliothèque header-only ?
Voici quelques questions qui peuvent vous aider à répondre à cette question :
-
Est ce que le code est fortement templaté ? Si oui, cela veut dire que la majorité de votre code est déjà dans des headers, alors pourquoi pas ?
-
Est ce qu’il y a des dépendances ? Si oui, ce n’est peut être pas une très bonne idée. Un des buts d’une bibliothèque header-only est d’être très facile à inclure dans un projet, or s’il y a d’autres dépendances, on perd cet avantage.
-
Quel est la taille du projet ? Si le projet est petit, cela ne va pas impacter les temps de compilations, donc ça ne devrait pas poser de soucis. Cependant, si la taille est conséquente, cela peut rendre le projet très long à compiler et devenir problématique.
Il y a aussi bien évidemment, comme évoqué précédemment, la possibilité de proposer une version supplémentaire “header-only” de sa bibliothèque.