Baduit

A young French developper who really likes (modern) C++

About me
14 April 2021

Les bibliothèques "header-only" avec CMake

by Baduit

Introduction

Dans cet article, je vais vous expliquer comment créer et se servir d’une bibliothèque header-only. Si vous ne savez pas ce que c’est, ou si vous avez besoin d’un petit rappel, je vous invite à lire cet article.

Créer une bibliothèques header-only

Pour en créer une, on utilise presque la même méthode que pour une bibliothèque statique ou une partagée. Lors de l’appel à la fonction add_library, il suffit d’utiliser le mot-clef INTERFACE au lieu de STATIC ou SHARED.

Une bonne pratique est d’utiliser la fonction target_sources, en arguments, les fichiers headers composant la bibliothèque. Cela leur permet d’apparaitre correctement lorsque le projet sera ouvert via un IDE, tel que Visual Studio par exemple.

Voici à quoi cela peut ressembler :

1
2
3
4
5
# Create a library named 'Awesome'
add_library(Awesome INTERFACE)

# It is composed of 2 headers
target_sources(Awesome INTERFACE Awesome.hpp MoreAwesome.hpp)

Utiliser une bibliothèques header-only

Si elle a été créée avec CMake

Dans ce cas là, c’est tout simple, il suffit d’appeler la fonction target_link_libraries comme vous le feriez pour n’importe quelle bibliothèque. Voici un exemple :

1
2
3
4
# Create a basic target
add_executable(my_exe main.cpp)
# Let's imagine the library is called header_lib and use it
target_link_libraries(my_exe header_lib)

Bien évidemment, comme pour inclure n’importe quel autre type de bibliothèque, vous devrez soit inclure le fichier CMakeLists.txt de la bibliothèque avec la fonction add_subdirectory, soit sinon chercher directement la bibliothèque avec find_library, soit chercher le package avec find_package

Si elle n’a pas été créée avec CMake

La solution la plus simple est d’ajouter le dossier contenant les fichiers composant la bibliothèque dans le path où le compilateur cherchera les headers. Pour cela, c’est très simple, il suffit d’appeler la fonction target_include_directories.

1
2
3
add_executable(my_exe main.cpp)
# The source of the library are in the folder 'path/to/lib_sources'
target_include_directories(my_exe PRIVATE path/to/lib_sources)

Une autre solution un peu plus propre (et qui reste très simple) est de créer une bibliothèque avec cmake à partir des sources, comme vu au début de l’article. Ensuite, il suffit d’utiliser cette bibliothèque comme vu précédemment.

Exemple complet

Imaginons un projet composé d’une bibliothèque header-only et d’un exécutable qui utilise cette bibliothèque. L’arborescence des fichiers est la suivante :

.
├── CMakeLists.txt
├── lib
│   ├── CMakeLists.txt
│   └── Commentinator
│       ├── Commentinator.hpp
│       └── Impl.hpp
└── src
    └── main.cpp

Commençons par le contenu du CMakeLists.txt de la bibliothèque.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Create the library
add_library(Commentinator INTERFACE)

# Add the sources for a better integrations in the IDE
target_sources(Commentinator INTERFACE
               ${CMAKE_CURRENT_LIST_DIR}/Commentinator/Commentinator.hpp
			   ${CMAKE_CURRENT_LIST_DIR}/Commentinator/Impl.hpp)

# This library requires c++17
target_compile_features(Commentinator INTERFACE cxx_std_17)

# And the most important part set the include directories
target_include_directories(Commentinator INTERFACE ${CMAKE_CURRENT_LIST_DIR})

Et maintenant, celui de l’exécutable qui va utiliser cette bibliothèque :

1
2
3
4
5
6
7
8
9
10
11
12
# Basic stuff every CMake project need
cmake_minimum_required(VERSION 3.12)
project(bestProjectEver VERSION 1.2.0 DESCRIPTION "The best project ever created in the world.")

# Include the CMakeLists.txt of the library
add_subdirectory(lib)

# Create the executable named `example`
add_executable(example ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp)

# Use the library
target_link_libraries(example Commentinator)

Et voilà, vous avez maintenant toutes les clefs en main pour utiliser et créer des bibliothèques header-only !

Sources

tags: C++ - CMake