u/Adept-Restaurant-541

▲ 32 r/cpp

There are already many excellent C++ CLI parsers out there, but most of them still revolve around mutable runtime builder APIs.

Most existing parsers look something like this:

CLI::App app{"example"};

std::string output;
bool verbose = false;
std::vector<std::string> inputs;

app.add_option("-o,--output", output, "output file");
app.add_flag("-v,--verbose", verbose, "verbose mode");
app.add_option("inputs", inputs, "input files")->required();

CLI11_PARSE(app, argc, argv);

Why are duplicate option names still runtime errors in C++ CLI parsers? Where is the compile-time validation?

Why is the command schema still built through runtime mutation? Many CLI schemas are effectively static. Why not treat them as a static schema and let the compiler enforce it?

One of the things I love about C++ is its ability to express intent and constraints in the type system and let the compiler enforce them.

But many C++ CLI parsers still rely heavily on runtime mutation and stringly-typed APIs.

Rust has clap, which is a great typed CLI parser. So what about C++?

So I wanted to explore what that direction could look like in modern C++20.

I built a new C++ CLI parser that takes a different approach:

#include <cli/cli.hh>

struct Args {
    cli::Flag<"verbose", 'v'> verbose;
    cli::StringOption<"output", 'o'> output;
    cli::Positional<std::string, cli::nargs::one_or_more> inputs;
};

using namespace std;

auto main(int argc, char** argv) -> int {
    // Returns parsed Args or exits with an error message.
    const auto args = cli::parseOrExit<Args>(argc, argv);

    std::cout << "verbose: " << args.verbose.value() << '\n';

    if (args.output.has_value()) {
        std::cout << "output: " << *args.output.value() << '\n';
    }

    for (const auto& input : args.inputs.value()) {
        std::cout << input << '\n';
    }
}

Try this out on Godbolt: https://godbolt.org/z/53d8rEMoP

The goal is to explore a C++20-native parser API where the command line is represented as a typed schema rather than a mutable runtime builder.

The interesting part for me is that this works in plain C++20, without reflection, macros, code generation, or external tooling.

Repository: https://github.com/CLI20-dev/cli20

Still early-stage. I'm mainly looking for feedback on API ergonomics, diagnostics, and the compile-time/runtime tradeoff.

u/Adept-Restaurant-541 — 8 days ago