reversed(top()) code tags rss about

How to limit flag occurence count in CLI11

June 16, 2024
[c++] [c++11] [programming]

There exists a quite popular command-line parser for C++ called CLI11. It’s not without issues (e.g., compilation time is quite bad), however its API is probably one of the most convenient out there (really weird that other similar libraries aren’t doing the same).

I’m using it for a project and had a need for a flag that can be repeated but I wanted to limit number of times that it can be repeated. Couldn’t find any information on how to do this, but managed to figure it out, hence this note.

What I’m talking about

You’ve probably seen this in case of increasing level of verbosity in something like ssh where adding more -v to a command line (doesn’t matter if it’s -v -v or a more typical -vv) results in more detailed logging.

CLI11 calls them integer flags and they use integer variables. But value of that variable can’t be explicitly constrained by things like ->expected() or validators as they deal with option values and flags don’t have them.

How to enforce count of such flags

You can obviously just check the value after a successful parsing, but that could mean inconsistency in how errors get reported to the user and a probability that you forget to the check on some code path.

The solution is to use a custom validator function but instead of checking for a value that it gets passed, count number of its invocations:

cmd->add_flag("-v,--verbose", verbosity, "How much output you want")
   ->default_str("")
   ->check([n = 0](const std::string &) mutable {
       return (++n > 2 ? "can't be given more than twice" : "");
   });

->default_str("") is to prevent default value of 0 from appearing in --help output. [n = 0] is C++14 version to avoid declaring extra variables, in C++11 that would be:

int n = 0;

cmd->add_flag("-v,--verbose", verbosity, "How much output you want")
   ->default_str("")
   ->check([n](const std::string &) mutable {
       return (++n > 2 ? "can't be given more than twice" : "");
   });

The function set via ->check(...) should return non-empty string on error which it does if count is too large. The drawback is that this assumes the check function isn’t called more than once per specific flag on command-line, which seems to be the case.

The error message on incorrect invocation looks as follows:

--verbose: can't be given more than twice
Run with --help for more information.