🔝 Retour au Sommaire
Cette sous-section couvre l'intégration de CLI11 dans un projet C++ sur Ubuntu, de l'installation à la compilation d'un premier programme fonctionnel. CLI11 étant header-only, l'installation se résume à rendre les headers disponibles pour votre compilateur. Plusieurs méthodes existent, de la plus simple (copie d'un fichier unique) à la plus intégrée (CMake FetchContent).
C'est la méthode recommandée pour tout projet utilisant CMake. FetchContent télécharge CLI11 au moment de la configuration et l'intègre comme une dépendance de première classe, avec gestion automatique des versions.
mon-outil/
├── CMakeLists.txt
├── src/
│ └── main.cpp
└── build/ # Créé lors de la compilation
cmake_minimum_required(VERSION 3.16)
project(mon-outil VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# --- Dépendance CLI11 via FetchContent ---
include(FetchContent)
FetchContent_Declare(
cli11
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v2.6.0 # Vérifier la dernière version stable
)
FetchContent_MakeAvailable(cli11)
# --- Exécutable ---
add_executable(mon-outil src/main.cpp)
target_link_libraries(mon-outil PRIVATE CLI11::CLI11)
# --- Bonnes pratiques : warnings stricts ---
target_compile_options(mon-outil PRIVATE
-Wall -Wextra -Wpedantic -Werror
)Points importants :
CLI11::CLI11est la target CMake exportée par CLI11. C'est la seule chose à lier — les include paths sont configurés automatiquement.GIT_TAGpointe vers un tag de release. Ne pointez jamais versmaindans un projet de production : épinglez toujours une version précise pour la reproductibilité des builds (voir section 27.2 sur Conan et la gestion des dépendances).- CLI11 étant header-only,
target_link_librariesne lie aucune librairie binaire — il ne fait qu'ajouter les chemins d'inclusion.
cd mon-outil
cmake -B build -G Ninja # Configuration avec le générateur Ninja
cmake --build build # Compilation
./build/mon-outil --help # Test💡 Ninja comme générateur : nous recommandons
-G Ninjaplutôt que le Makefile par défaut pour ses temps de build plus rapides, particulièrement sur les projets multi-fichiers (voir section 26.5 et section 28.3).
Lors du premier cmake -B build, CMake clone le dépôt CLI11. Ce téléchargement n'intervient qu'une seule fois — les exécutions suivantes utilisent le cache. Si vous travaillez dans un environnement CI, pensez à mettre en cache le répertoire build/_deps/ pour accélérer les builds (voir section 38.3 sur l'accélération CI).
Si votre projet utilise déjà Conan pour la gestion des dépendances, CLI11 est disponible dans le dépôt central ConanCenter.
[requires]
cli11/2.4.2
[generators]
CMakeDeps
CMakeToolchain cmake_minimum_required(VERSION 3.16)
project(mon-outil VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(CLI11 REQUIRED)
add_executable(mon-outil src/main.cpp)
target_link_libraries(mon-outil PRIVATE CLI11::CLI11) cd mon-outil
conan install . --output-folder=build --build=missing
cmake -B build --preset conan-release
cmake --build build La target CMake reste identique (CLI11::CLI11) — seul le mécanisme de résolution change. Le choix entre FetchContent et Conan est une question d'écosystème de projet : si vous gérez déjà plusieurs dépendances avec Conan, restez cohérent. Pour un projet autonome avec CLI11 comme seule dépendance externe, FetchContent est plus simple.
Avec le gestionnaire de paquets de Microsoft :
vcpkg install cli11Puis dans CMake, en utilisant le toolchain file vcpkg :
find_package(CLI11 REQUIRED)
target_link_libraries(mon-outil PRIVATE CLI11::CLI11) cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
cmake --build build CLI11 est packagé dans les dépôts Ubuntu :
sudo apt install libcli11-devCette méthode est la plus rapide pour démarrer, mais présente un inconvénient : la version disponible dans les dépôts Ubuntu peut être en retard par rapport aux releases officielles. Vérifiez la version installée :
apt show libcli11-dev | grep VersionL'intégration CMake est identique :
find_package(CLI11 REQUIRED)
target_link_libraries(mon-outil PRIVATE CLI11::CLI11)
⚠️ En production, préférez FetchContent ou Conan pour maîtriser la version exacte de la dépendance. Le paquet système est adapté au prototypage rapide ou aux environnements de développement local.
Pour les cas où vous voulez zéro infrastructure de dépendance, CLI11 fournit un header unique consolidé. Cette approche est adaptée aux petits utilitaires autonomes ou aux situations où le réseau n'est pas disponible au moment du build.
mkdir -p mon-outil/external
cd mon-outil/external
wget https://github.com/CLIUtils/CLI11/releases/download/v2.6.0/CLI11.hpp Structure résultante :
mon-outil/
├── CMakeLists.txt
├── external/
│ └── CLI11.hpp
└── src/
└── main.cpp
CMakeLists.txt minimal :
cmake_minimum_required(VERSION 3.16)
project(mon-outil VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(mon-outil src/main.cpp)
target_include_directories(mon-outil PRIVATE external) Dans ce cas, l'inclusion dans le code source utilise des guillemets plutôt que des chevrons :
#include "CLI11.hpp" // Header unique local
// au lieu de :
// #include <CLI/CLI.hpp> // Installation via package ou FetchContent💡 Versionnement : si vous commitez le fichier
CLI11.hppdans votre dépôt, documentez la version dans un fichierexternal/VERSIONS.mdou un commentaire. Une dépendance dont on ne connaît pas la version est une dette technique silencieuse.
| Méthode | Avantages | Inconvénients | Cas d'usage |
|---|---|---|---|
| FetchContent | Version épinglée, intégré CMake, rien à installer | Nécessite un accès réseau au premier build | Nouveau projet CMake (recommandé) |
| Conan | Gestion centralisée, profils multi-plateformes | Configuration Conan initiale | Projet avec plusieurs dépendances Conan |
| vcpkg | Intégration Microsoft, large catalogue | Toolchain file requis | Projet multi-plateforme avec vcpkg |
| apt | Installation en une commande | Version potentiellement datée | Prototypage rapide |
| Header unique | Zéro dépendance, fonctionne hors-ligne | Version figée manuellement | Petit utilitaire, environnement contraint |
Mettons en pratique avec un programme réaliste — un outil qui vérifie la disponibilité d'un endpoint HTTP. Ce premier exemple illustre les fondamentaux de CLI11 : options typées, valeurs par défaut, validation et aide automatique.
#include <CLI/CLI.hpp>
#include <print>
#include <string>
#include <vector>
#include <chrono>
int main(int argc, char* argv[]) {
// --- Création de l'application ---
CLI::App app{"httpcheck — Vérification de disponibilité HTTP"};
// Afficher la version avec --version
app.set_version_flag("--version,-V", "httpcheck 1.0.0");
// --- Déclaration des options ---
std::string url;
app.add_option("url", url, "URL à vérifier")
->required();
int timeout_ms = 5000;
app.add_option("--timeout,-t", timeout_ms, "Timeout en millisecondes")
->check(CLI::Range(100, 60'000))
->default_str("5000");
int retries = 3;
app.add_option("--retries,-r", retries, "Nombre de tentatives")
->check(CLI::Range(1, 20));
int interval_ms = 1000;
app.add_option("--interval,-i", interval_ms, "Intervalle entre tentatives (ms)")
->check(CLI::Range(100, 30'000));
bool verbose = false;
app.add_flag("--verbose,-v", verbose, "Afficher les détails de chaque tentative");
bool quiet = false;
app.add_flag("--quiet,-q", quiet, "Afficher uniquement le résultat final");
// verbose et quiet sont mutuellement exclusifs
app.get_option("--verbose")->excludes(app.get_option("--quiet"));
app.get_option("--quiet")->excludes(app.get_option("--verbose"));
std::string format = "text";
app.add_option("--format,-f", format, "Format de sortie")
->check(CLI::IsMember({"text", "json"}));
std::vector<std::string> headers;
app.add_option("--header,-H", headers,
"Headers HTTP (répétable, format Clé:Valeur)");
// --- Parsing ---
CLI11_PARSE(app, argc, argv);
// --- Logique métier ---
// (Dans un vrai programme, ici se trouverait l'appel HTTP)
if (verbose) {
std::println("Configuration :");
std::println(" URL : {}", url);
std::println(" Timeout : {} ms", timeout_ms);
std::println(" Tentatives: {}", retries);
std::println(" Intervalle: {} ms", interval_ms);
std::println(" Format : {}", format);
for (const auto& h : headers) {
std::println(" Header : {}", h);
}
}
if (format == "json") {
std::println(R"({{"url":"{}","status":"ok","attempts":1}})", url);
} else {
if (!quiet) {
std::println("✓ {} — accessible (1/{} tentatives)", url, retries);
}
}
return 0;
}Compilez et testez les différents scénarios :
# Build
cmake -B build -G Ninja && cmake --build build
# Usage basique
$ ./build/httpcheck https://example.com
✓ https://example.com — accessible (1/3 tentatives)
# Mode verbeux avec options personnalisées
$ ./build/httpcheck https://api.example.com -v --timeout 10000 -r 5 \
-H "Authorization:Bearer tok123" -H "Accept:application/json"
Configuration :
URL : https://api.example.com
Timeout : 10000 ms
Tentatives: 5
Intervalle: 1000 ms
Format : text
Header : Authorization:Bearer tok123
Header : Accept:application/json
✓ https://api.example.com — accessible (1/5 tentatives)
# Sortie JSON (exploitable dans un pipeline)
$ ./build/httpcheck https://example.com --format json
{"url":"https://example.com","status":"ok","attempts":1}
# Sortie JSON + jq
$ ./build/httpcheck https://example.com -f json | jq '.status'
"ok"
# Version
$ ./build/httpcheck --version
httpcheck 1.0.0
# Validation : timeout hors limites
$ ./build/httpcheck https://example.com --timeout 0
Error: --timeout: Value 0 not in range [100, 60000]
Run with --help for more information.
# Validation : format inconnu
$ ./build/httpcheck https://example.com --format xml
Error: --format: Check CLI::IsMember FAILED
# Exclusion mutuelle : verbose + quiet
$ ./build/httpcheck https://example.com -v -q
Error: --verbose: excludes --quiet
Run with --help for more information.
# Aide auto-générée
$ ./build/httpcheck --help
httpcheck — Vérification de disponibilité HTTP
Usage: httpcheck [OPTIONS] url
Positionals:
url TEXT REQUIRED URL à vérifier
Options:
-h,--help Print this help message and exit
--version,-V Display program version information and exit
--timeout,-t INT:INT in [100 - 60000] [5000]
Timeout en millisecondes
--retries,-r INT:INT in [1 - 20]
Nombre de tentatives
--interval,-i INT:INT in [100 - 30000]
Intervalle entre tentatives (ms)
--verbose,-v Afficher les détails de chaque tentative
--quiet,-q Afficher uniquement le résultat final
--format,-f TEXT:{text,json} Format de sortie
--header,-H TEXT ... Headers HTTP (répétable, format Clé:Valeur)Reprenons les éléments de ce premier programme pour identifier les concepts CLI11 en jeu :
CLI::App app{"httpcheck — Vérification de disponibilité HTTP"};Chaque programme CLI11 commence par la création d'un objet CLI::App. La chaîne passée au constructeur est la description affichée dans l'aide. Cet objet est le conteneur central sur lequel toutes les options, flags et sous-commandes sont enregistrés.
app.add_option("--timeout,-t", timeout_ms, "Timeout en millisecondes");Le premier argument est la spécification de nommage : nom long, virgule, nom court. CLI11 gère les deux formes automatiquement. Le deuxième argument est une référence vers la variable de destination — c'est le binding direct. Le troisième est la description pour l'aide.
add_option retourne un pointeur CLI::Option* sur lequel on chaîne des modificateurs. C'est un pattern fluent :
app.add_option("--timeout,-t", timeout_ms, "Timeout")
->required() // L'option est obligatoire
->check(CLI::Range(100, 60000)) // Validation par intervalle
->default_str("5000"); // Texte affiché dans l'aideapp.add_flag("--verbose,-v", verbose, "Mode verbeux");Un flag est une option sans valeur : sa seule présence sur la ligne de commande met la variable à true. CLI11 distingue add_option (attend une valeur) de add_flag (présence/absence).
std::vector<std::string> headers;
app.add_option("--header,-H", headers, "Headers HTTP (répétable)"); Quand la variable de destination est un std::vector, CLI11 autorise automatiquement l'option à apparaître plusieurs fois. Chaque occurrence ajoute une entrée au vecteur. C'est le pattern standard pour les options comme -H de curl.
CLI11_PARSE(app, argc, argv);Cette macro encapsule l'appel à app.parse(argc, argv) dans un bloc try/catch. En cas d'erreur de parsing, elle affiche le message d'erreur et quitte avec le code de retour approprié. En cas de --help ou --version, elle affiche l'information demandée et quitte avec le code 0.
Pour un contrôle plus fin, vous pouvez remplacer la macro par un parsing explicite :
try {
app.parse(argc, argv);
} catch (const CLI::ParseError& e) {
return app.exit(e);
}Cela permet par exemple de réaliser un nettoyage ou un logging avant la sortie en cas d'erreur. En pratique, CLI11_PARSE suffit dans la grande majorité des cas.
app.set_version_flag("--version,-V", "httpcheck 1.0.0");Cette méthode enregistre un flag qui, lorsqu'il est invoqué, affiche la chaîne de version et termine le programme. La convention veut que le nom court soit -V (majuscule) pour éviter le conflit avec -v (verbose), mais c'est configurable.
💡 Bonne pratique : dans un vrai projet, la version vient de CMake, pas d'une constante en dur. On utilise
configure_filepour injecterPROJECT_VERSIONdans un header :# CMakeLists.txt configure_file(src/version.h.in src/version.h) target_include_directories(mon-outil PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)// src/version.h.in #pragma once #define APP_VERSION "@PROJECT_VERSION@"// src/main.cpp #include "version.h" app.set_version_flag("--version,-V", APP_VERSION);Ainsi, la version est définie à un seul endroit (
project(... VERSION 1.0.0)) et propagée partout automatiquement.
Pour confirmer que CLI11 est correctement intégré à votre projet, un test simple consiste à compiler un programme minimal et à vérifier que l'aide est bien générée :
// test_cli11.cpp
#include <CLI/CLI.hpp>
int main(int argc, char* argv[]) {
CLI::App app{"Test CLI11"};
CLI11_PARSE(app, argc, argv);
return 0;
}$ g++ -std=c++23 -I/chemin/vers/cli11/include test_cli11.cpp -o test_cli11
$ ./test_cli11 --help
Test CLI11
Usage: test_cli11 [OPTIONS]
Options:
-h,--help Print this help message and exitSi ce programme compile et affiche l'aide, votre installation est fonctionnelle. Vous êtes prêt à passer aux sections suivantes, où nous explorerons en profondeur les options, flags, sous-commandes (36.1.2), la validation (36.1.3) et la personnalisation de l'aide (36.1.4).