CLI11
A command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
GitHub Overview
CLIUtils/CLI11
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
Topics
Star History
Framework
CLI11
Overview
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface. It comes in a single header-only file form for easy inclusion in projects. It's easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks. Works on Mac, Linux, and Windows, with 100% test coverage on all three systems.
Details
CLI11 was originally used by the GooFit GPU fitting framework and is tested on Azure Actions and GitHub Actions. It was inspired by plumbum.cli for Python.
Latest Updates (2024-2025)
CLI11 continues to be actively developed with the following recent features:
- Pre-compiled Mode: CLI11_PRECOMPILED option allows pre-compilation of the library, saving time on incremental rebuilds
- Empty Vector Output Support: Support for output of empty vectors
- Summing Option Policy: A summing option policy that can be applied more broadly
- Optional Arguments Validation: Option to validate optional arguments to discriminate from positional arguments
- Default Path Validator: New validator to check for files on a default path for configuration files or other file arguments
Key Features
- Header-Only: Simply drop in a single header file (CLI11.hpp) to use
- Rich Data Type Support: Supports string, int, double, bool, vector, variant, and more
- Subcommands: Organize application functionality logically
- Options and Flags: Supports both short (-v) and long (--verbose) forms
- Positional Arguments: Required, optional, and array arguments supported
- Automatic Help Generation: Automatically generates help messages from command-line arguments
- Validators: Built-in input validation functionality
- Configuration Files: Supports TOML, INI, and JSON configuration files
- Shell Completion: Auto-generates completion scripts for Bash, Zsh, Fish, and PowerShell
- C++11 Compatible: Uses modern C++ features while remaining compatible with C++11+
Major Components
- CLI::App: Main application class
- Options: Command-line option definition and processing
- Arguments: Positional argument processing
- Validators: Input value validation
- Config: Configuration file loading and processing
- Formatter: Help message formatting
Pros and Cons
Pros
- Easy Integration: Header-only, just include a single file
- High Performance: Fast execution through compile-time optimization
- Rich Features: Validation, subcommands, configuration file support, etc.
- Modern C++: Intuitive API using C++11+ features
- Cross-Platform: Full support for Windows, Linux, and macOS
- Well-Tested: 100% test coverage for high reliability
- Active Development: Continuous feature additions and bug fixes
- Pre-compile Support: Reduced build times for large projects
Cons
- C++11 Dependency: Cannot be used with older compilers
- Learning Curve: Rich feature set requires time to fully understand
- Header-Only Constraints: May increase compile times in large projects (mitigated by pre-compile mode)
- Scattered Documentation: Finding specific information can take time due to extensive features
Key Links
Code Examples
Basic Usage
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"MyApp - A simple CLI application"};
std::string filename = "default.txt";
app.add_option("-f,--file", filename, "File to process");
CLI11_PARSE(app, argc, argv);
std::cout << "Processing file: " << filename << std::endl;
return 0;
}
Flags and Options Example
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"Configurable Application"};
// Flag (boolean value)
bool verbose = false;
app.add_flag("-v,--verbose", verbose, "Enable verbose output");
// Required option
std::string name;
app.add_option("-n,--name", name, "User name")->required();
// Option with default value
int count = 1;
app.add_option("-c,--count", count, "Execution count", true);
// Multi-value option
std::vector<std::string> files;
app.add_option("-f,--files", files, "Files to process");
CLI11_PARSE(app, argc, argv);
if (verbose) {
std::cout << "Verbose mode enabled" << std::endl;
}
std::cout << "User name: " << name << std::endl;
std::cout << "Execution count: " << count << std::endl;
if (!files.empty()) {
std::cout << "Files to process:" << std::endl;
for (const auto& file : files) {
std::cout << " - " << file << std::endl;
}
}
return 0;
}
Subcommands Example
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"File Management Tool"};
// list subcommand
auto list_cmd = app.add_subcommand("list", "Display file list");
bool show_hidden = false;
list_cmd->add_flag("-a,--all", show_hidden, "Show hidden files too");
list_cmd->callback([&]() {
std::cout << "Displaying file list";
if (show_hidden) std::cout << " (including hidden files)";
std::cout << std::endl;
});
// copy subcommand
auto copy_cmd = app.add_subcommand("copy", "Copy files");
std::string source, dest;
copy_cmd->add_option("source", source, "Source file")->required();
copy_cmd->add_option("dest", dest, "Destination file")->required();
bool force = false;
copy_cmd->add_flag("-f,--force", force, "Force overwrite");
copy_cmd->callback([&]() {
std::cout << "Copying from " << source << " to " << dest;
if (force) std::cout << " (force overwrite)";
std::cout << std::endl;
});
// delete subcommand
auto delete_cmd = app.add_subcommand("delete", "Delete files");
std::vector<std::string> targets;
delete_cmd->add_option("files", targets, "Files to delete")->required();
bool recursive = false;
delete_cmd->add_flag("-r,--recursive", recursive, "Recursive deletion");
delete_cmd->callback([&]() {
for (const auto& target : targets) {
std::cout << "Deleting " << target;
if (recursive) std::cout << " (recursive)";
std::cout << std::endl;
}
});
CLI11_PARSE(app, argc, argv);
return 0;
}
Validation Example
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"Validation Example"};
// File existence check
std::string input_file;
app.add_option("-i,--input", input_file, "Input file")
->required()
->check(CLI::ExistingFile);
// Number range check
int port;
app.add_option("-p,--port", port, "Port number")
->required()
->check(CLI::Range(1024, 65535));
// Custom validator
std::string email;
app.add_option("-e,--email", email, "Email address")
->check([](const std::string& str) {
if (str.find('@') == std::string::npos) {
return "Not a valid email address";
}
return std::string();
});
// Choice restriction
std::string format;
app.add_option("-f,--format", format, "Output format")
->check(CLI::IsMember({"json", "xml", "csv", "yaml"}));
CLI11_PARSE(app, argc, argv);
std::cout << "Input file: " << input_file << std::endl;
std::cout << "Port: " << port << std::endl;
std::cout << "Email: " << email << std::endl;
std::cout << "Format: " << format << std::endl;
return 0;
}
Configuration File Support
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"Configuration File App"};
// Configuration file option
std::string config_file = "config.ini";
app.add_option("-c,--config", config_file, "Configuration file")
->check(CLI::ExistingFile);
// Various configuration items
std::string host = "localhost";
app.add_option("--host", host, "Connection host");
int port = 8080;
app.add_option("--port", port, "Connection port");
bool ssl = false;
app.add_flag("--ssl", ssl, "Use SSL connection");
std::vector<std::string> headers;
app.add_option("--header", headers, "Additional HTTP headers");
// Load configuration file
try {
app.set_config("--config", config_file, "Configuration file", false);
} catch(const CLI::FileError &e) {
std::cout << "Failed to load configuration file: " << e.what() << std::endl;
}
CLI11_PARSE(app, argc, argv);
std::cout << "Connection: " << host << ":" << port << std::endl;
std::cout << "SSL: " << (ssl ? "enabled" : "disabled") << std::endl;
if (!headers.empty()) {
std::cout << "Additional headers:" << std::endl;
for (const auto& header : headers) {
std::cout << " " << header << std::endl;
}
}
return 0;
}
Pre-compiled Mode Usage
# CMakeLists.txt
cmake_minimum_required(VERSION 3.11)
project(MyProject)
find_package(CLI11 CONFIG REQUIRED)
# Enable pre-compiled mode
set(CLI11_PRECOMPILED ON)
add_executable(my_app main.cpp)
target_link_libraries(my_app CLI11::CLI11)
Error Handling Example
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"Error Handling Example"};
std::string input_file;
app.add_option("-i,--input", input_file, "Input file")
->required()
->check(CLI::ExistingFile);
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
// Handle CLI parse errors
return app.exit(e);
} catch (const CLI::Success &e) {
// Normal exit due to --help or --version
return CLI::ExitCodes::Success;
} catch (const std::exception &e) {
// Other exceptions
std::cerr << "An error occurred: " << e.what() << std::endl;
return CLI::ExitCodes::BaseClass;
}
// Main processing
std::cout << "Processing file: " << input_file << std::endl;
return CLI::ExitCodes::Success;
}
Single File Usage
# Download single header file from latest release
wget https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp
# Or use curl
curl -LO https://github.com/CLIUtils/CLI11/releases/latest/download/CLI11.hpp
// main.cpp - Single file usage
#include "CLI11.hpp"
#include <iostream>
int main(int argc, char** argv) {
CLI::App app{"Single File App"};
std::string message = "Hello, World!";
app.add_option("-m,--message", message, "Message to display");
CLI11_PARSE(app, argc, argv);
std::cout << message << std::endl;
return 0;
}
# Compile and run
g++ -std=c++11 main.cpp -o myapp
./myapp --message "Hello, CLI11!"