
Define your CLI as a type, not as a builder.
#include "cli/argument.hh"
#include "cli/parser.hh"
struct Args {
cli::Flag<"verbose", 'v'> verbose;
cli::StringOption<"output", 'o'> output;
cli::Positional<std::string,
cli::nargs::one_or_more> inputs;
};
auto main(int argc, char* argv[]) -> int {
const auto args = cli::parse_or_exit<Args>(argc, argv);
}
📦
Header-only
Drop include/ into your project. No linking, no runtime overhead.
⚡
C++20 native
Compile-time option names, concepts, constexpr. Built for modern C++.
🔒
Type-safe schema
Your CLI is a struct. No string lookups at runtime.
✨
Zero macros
Clean modern C++. No registration macros anywhere.
🌿
Subcommands
Nested structs compose naturally. Recursive by design.
📖
Auto help
Add cli::Help<> help; and get full formatted help output.
CLI11 → CLI20
Before (CLI11)
CLI::App app{"my app"};
bool verbose = false;
std::string output;
app.add_flag("-v,--verbose", verbose);
app.add_option("-o,--output", output);
CLI11_PARSE(app, argc, argv);
After (CLI20)
struct Args {
cli::Flag<"verbose", 'v'> verbose;
cli::StringOption<"output", 'o'> output;
};
const auto args =
cli::parse_or_exit<Args>(argc, argv);