Skip to content

Repository files navigation

libcli

CILicense: MITC++ StandardHeader only

A header-only C++26 command-line argument parser driven entirely by static reflection. Describe your options as a plain struct, attach behaviour with annotations, and cli::parse does the rest — no macros, no builder boilerplate, no runtime registration.

#include<cli/cli.hpp>
#include<print>struct [[=cli::program("greet"),
=cli::version("0.1.0"),
=cli::about("Say hello to someone.")]]
Opts {
[[=cli::desc("Who to greet"), =cli::positional, =cli::required]]
std::string who;
[[=cli::desc("Greeting volume (0-11)"),
=cli::short_name('v'),
=cli::value_name("N"),
=cli::range(0, 11)]]
int volume = 5;
[[=cli::desc("Use SHOUTING"), =cli::short_name('s')]]
bool shout = false;
};
intmain(int argc, char** argv) {
auto opts = cli::parse<Opts>(argc, argv);
auto msg = std::format("Hello, {}!", opts.who);
if (opts.shout) std::transform(msg.begin(), msg.end(), msg.begin(), ::toupper);
std::println("{}", msg);
}
$ greet --help
greet
Say hello to someone.
Usage: greet [options]
Positional arguments:
who Who to greet
Options:
-v, --volume N Greeting volume (0-11)
-s, --shout Use SHOUTING

Why libcli?

libcliCLI11argparsecxxopts
Options struct is the source of truth❌ (builder API)❌ (builder API)❌ (builder API)
Zero macros
No runtime registration step
Subcommands via std::variant❌ (subparser API)
Help and parse table generated at compile time
Standard requiredC++26C++11C++17C++11

The trade-off is at the bottom row: libcli needs a compiler with P2996 static reflection, which today means GCC 16.1 or newer. In exchange, every option name, type, default, and validator is derived from your struct at compile time, so there is no schema/code drift to keep in sync.

Status

Pre-release (0.0.x). The annotation surface is stable; the parser implementation is still filling in features tagged // TODO (see the CHANGELOG for what's wired and what's stubbed). Feedback and PRs welcome.

Install

FetchContent (recommended)

include(FetchContent)
FetchContent_Declare(
libcli
GIT_REPOSITORY https://github.com/aaronaranda/libcli.git
GIT_TAG main)
FetchContent_MakeAvailable(libcli)
target_link_libraries(my_appPRIVATElibcli::cli)

find_package (after cmake --install)

find_package(libcli0.1REQUIRED)
target_link_libraries(my_appPRIVATElibcli::cli)

Build options:

OptionDefaultEffect
LIBCLI_BUILD_TESTSOFFBuild googletest-based test suite.
LIBCLI_BUILD_EXAMPLESOFFBuild the example programs under examples/.
LIBCLI_BUILD_DOCSOFFGenerate Doxygen HTML into build/docs/html/.
LIBCLI_BUILD_BENCHMARKSOFFBuild benchmark suite (currently empty).

Requirements

  • A C++26 compiler with static reflection support (-freflection):
    • GCC ≥ 16.1 — known good.
    • Clang/MSVC do not yet implement P2996.
  • CMake ≥ 4.3.

Feature matrix

FeatureStatus
Positional arguments
Required vs optional members
Short (-v) and long (--volume) flags
Boolean flags (presence → true)
Repeatable counter flags (-vvv)
Vector-typed options (multi-value)
--name=value form
Subcommands via std::variant<...>
--help / -h auto-routing per subcommand
Program-level program/version/about/epilog/usage annotations
cli::range(min, max) validator⏳ stubbed, not enforced
cli::choices(...) validator⏳ stubbed, not enforced
cli::validate_with<&fn>⏳ stubbed
cli::on_match<&fn> action callbacks⏳ stubbed
cli::env("VAR") env-var fallback⏳ stubbed
Clustered shorts (-abc = -a -b -c)
std::optional<T> member type
try_parse<T> returning std::expected

See CHANGELOG.md for what landed in each release.

Documentation

  • Guide — narrative walk through the annotation surface.
  • Examples — small programs covering positional, flags, subcommands, etc.
  • Doxygen HTML — enable -DLIBCLI_BUILD_DOCS=ON and build the docs target.

Contributing

PRs and issues welcome. See CONTRIBUTING.md for build/test instructions and style notes.

License

MIT.

About

C++26 CLI library with reflection

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages