AI prompts
base on CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface. # CLI11: Command line parser for C++11
![CLI11 Logo](./docs/CLI11_300.png)
[![Build Status Azure][azure-badge]][azure]
[![Actions Status][actions-badge]][actions-link]
[![Code Coverage][codecov-badge]][codecov]
[![Codacy Badge][codacy-badge]][codacy-link]
[![License: BSD][license-badge]](./LICENSE) [![DOI][doi-badge]][doi-link]
[![Gitter chat][gitter-badge]][gitter]
[![Latest GHA release][releases-badge]][github releases]
[![Latest release][repology-badge]][repology]
[![Conan.io][conan-badge]][conan-link]
[![Conda Version][conda-badge]][conda-link]
[![Try CLI11 2.4 online][wandbox-badge]][wandbox-link]
[What's new](./CHANGELOG.md) β’ [Documentation][gitbook] β’ [API
Reference][api-docs]
CLI11 is a command line parser for C++11 and beyond that provides a rich feature
set with a simple and intuitive interface.
## Table of Contents
- [CLI11: Command line parser for C++11](#cli11-command-line-parser-for-c11)
- [Table of Contents](#table-of-contents)
- [Background](#background)
- [Introduction](#introduction)
- [Why write another CLI parser?](#why-write-another-cli-parser)
- [Other parsers](#other-parsers)
- [Features not supported by this library](#features-not-supported-by-this-library)
- [Install](#install)
- [Usage](#usage)
- [Adding options](#adding-options)
- [Option types](#option-types)
- [Example](#example)
- [Option options](#option-options)
- [Validators](#validators)
- [Transforming Validators](#transforming-validators)
- [Validator operations](#validator-operations)
- [Custom Validators](#custom-validators)
- [Querying Validators](#querying-validators)
- [Getting results](#getting-results)
- [Subcommands](#subcommands)
- [Subcommand options](#subcommand-options)
- [Callbacks](#callbacks)
- [Option groups](#option-groups)
- [Configuration file](#configuration-file)
- [Inheriting defaults](#inheriting-defaults)
- [Formatting](#formatting)
- [Subclassing](#subclassing)
- [How it works](#how-it-works)
- [Unicode support](#unicode-support)
- [Note on using Unicode paths](#note-on-using-unicode-paths)
- [Utilities](#utilities)
- [Other libraries](#other-libraries)
- [API](#api)
- [Examples](#examples)
- [Contribute](#contribute)
- [License](#license)
Features that were added in the last released minor version are marked with
"π". Features only available in main are marked with "π§".
## Background
### Introduction
CLI11 provides all the features you expect in a powerful command line parser,
with a beautiful, minimal syntax and no dependencies beyond C++11. It is header
only, and comes in a single file form for easy inclusion in projects. It is easy
to use for small projects, but powerful enough for complex command line
projects, and can be customized for frameworks. It is tested on [Azure][] and
[GitHub Actions][actions-link], and was originally used by the [GooFit GPU
fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for
Python. CLI11 has a user friendly introduction in this README, a more in-depth
tutorial [GitBook][], as well as [API documentation][api-docs] generated by
Travis. See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for
current and past releases. Also see the [Version 1.0 post][], [Version 1.3
post][], [Version 1.6 post][], or [Version 2.0 post][] for more information.
You can be notified when new releases are made by subscribing to
<https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly,
or use the releases mode of the GitHub watching tool.
### Why write another CLI parser?
An acceptable CLI parser library should be all of the following:
- Easy to include (i.e., header only, one file if possible, **no external
requirements**).
- Short, simple syntax: This is one of the main reasons to use a CLI parser, it
should make variables from the command line nearly as easy to define as any
other variables. If most of your program is hidden in CLI parsing, this is a
problem for readability.
- C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang
3.4+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
- Work on Linux, macOS, and Windows.
- Well tested on all common platforms and compilers. "Well" is defined as having
good coverage measured by [CodeCov][].
- Clear help printing.
- Nice error messages.
- Standard shell idioms supported naturally, like grouping flags, a positional
separator, etc.
- Easy to execute, with help, parse errors, etc. providing correct exit and
details.
- Easy to extend as part of a framework that provides "applications" to users.
- Usable subcommand syntax, with support for multiple subcommands, nested
subcommands, option groups, and optional fallthrough (explained later).
- Ability to add a configuration file (`TOML`, `INI`, or custom format), and
produce it as well.
- Produce real values that can be used directly in code, not something you have
pay compute time to look up, for HPC applications.
- Work with common types, simple custom types, and extensible to exotic types.
- Permissively licensed.
### Other parsers
<details><summary>The major CLI parsers for C++ include, with my biased opinions: (click to expand)</summary><p>
| Library | My biased opinion |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Boost Program Options][] | A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO. [See this comparison.][cli11-po-compare] |
| [The Lean Mean C++ Option Parser][] | One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well. |
| [TCLAP][] | The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produces wrapped values. |
| [Cxxopts][] | C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar. |
| [DocOpt][] | Completely different approach to program options in C++11, you write the docs and the interface is generated. Too fragile and specialized. |
After I wrote this, I also found the following libraries:
| Library | My biased opinion |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [GFlags][] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
| [GetOpt][] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
| [ProgramOptions.hxx][] | Interesting library, less powerful and no subcommands. Nice callback system. |
| [Args][] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
| [Argument Aggregator][] | I'm a big fan of the [fmt][] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. |
| [Clara][] | Simple library built for the excellent [Catch][] testing framework. Unique syntax, limited scope. |
| [Argh!][] | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free. |
| [CLI][] | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active. |
| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. The author has several other interesting projects. |
| [lyra][] | a simple header only parser with composable options. Might work well for simple standardized parsing |
See [Awesome C++][] for a less-biased list of parsers. You can also find other single
file libraries at [Single file libs][].
</p></details>
<br/>
None of these libraries fulfill all the above requirements, or really even come
close. As you probably have already guessed, CLI11 does. So, this library was
designed to provide a great syntax, good compiler compatibility, and minimal
installation fuss.
### Features not supported by this library
There are some other possible "features" that are intentionally not supported by
this library:
- Completion of partial options, such as Python's `argparse` supplies for
incomplete arguments. It's better not to guess. Most third party command line
parsers for python actually reimplement command line parsing rather than using
argparse because of this perceived design flaw (recent versions do have an
option to disable it).
- Autocomplete: This might eventually be added to both Plumbum and CLI11, but it
is not supported yet.
## Install
To use, the most common methods are described here additional methods and
details are available at [installation][]:
- All-in-one local header: Copy `CLI11.hpp` from the [most recent
release][github releases] into your include directory, and you are set. This
is combined from the source files for every release. This includes the entire
command parser library, but does not include separate utilities (like `Timer`,
`AutoTimer`). The utilities are completely self contained and can be copied
separately.
- All-in-one global header: Like above, but copying the file to a shared folder
location like `/opt/CLI11`. Then, the C++ include path has to be extended to
point at this folder. With CMake 3.10+, use `include_directories(/opt/CLI11)`
- For other methods including using CMake, conan or vcpkg and some specific
instructions for GCC 8 or WASI see [installation][].
## Usage
### Adding options
To set up, add options, and run, your main function will look something like
this:
```cpp
int main(int argc, char** argv) {
CLI::App app{"App description"};
argv = app.ensure_utf8(argv);
std::string filename = "default";
app.add_option("-f,--file", filename, "A help string");
CLI11_PARSE(app, argc, argv);
return 0;
}
```
For more information about π`ensure_utf8` the section on
[Unicode support](#unicode-support) below. The π`ensure_utf8` function is only
available in main currently and not in a release.
<details><summary>Note: If you don't like macros, this is what that macro expands to: (click to expand)</summary><p>
```cpp
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
return app.exit(e);
}
```
The try/catch block ensures that `-h,--help` or a parse error will exit with the
correct return code (selected from `CLI::ExitCodes`). (The return here should be
inside `main`). You should not assume that the option values have been set
inside the catch block; for example, help flags intentionally short-circuit all
other processing for speed and to ensure required options and the like do not
interfere.
</p></details>
</br>
The initialization is just one line, adding options is just two each. The parse
macro is just one line (or 5 for the contents of the macro). After the app runs,
the filename will be set to the correct value if it was passed, otherwise it
will be set to the default. You can check to see if this was passed on the
command line with `app.count("--file")`.
#### Option types
While all options internally are the same type, there are several ways to add an
option depending on what you need. The supported values are:
```cpp
// Add options
app.add_option(option_name, help_str="")
app.add_option(option_name,
variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectors of any other supported type.
help_string="")
app.add_option_function<type>(option_name,
function <void(const type &value)>, // type can be any type supported by add_option
help_string="")
// char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values.
// There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
app.add_option<typename T, typename XC>(option_name,
T &output, // output must be assignable or constructible from a value of type XC
help_string="")
// Add flags
app.add_flag(option_name,
help_string="")
app.add_flag(option_name,
variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic, or string-like, or any singular object with a defined conversion from a string like add_option
help_string="")
app.add_flag_function(option_name,
function <void(std::int64_t count)>,
help_string="")
app.add_flag_callback(option_name,function<void(void)>,help_string="")
// Add subcommands
App* subcom = app.add_subcommand(name, description);
Option_group *app.add_option_group(name,description);
```
An option name may start with any character except ('-', ' ', '\n', and '!').
For long options, after the first character all characters are allowed except
('=',':','{',' ', '\n'). For the `add_flag*` functions '{' and '!' have special
meaning which is why they are not allowed. Names are given as a comma separated
string, with the dash or dashes. An option or flag can have as many names as you
want, and afterward, using `count`, you can use any of the names, with dashes as
needed, to count the options. One of the names is allowed to be given without
proceeding dash(es); if present the option is a positional option, and that name
will be used on the help line for its positional form. The string `++` is also
not allowed as option name due to its use as an array separator and marker on
config files.
The `add_option_function<type>(...` function will typically require the template
parameter be given unless a `std::function` object with an exact match is
passed. The type can be any type supported by the `add_option` function. The
function should throw an error (`CLI::ConversionError` or `CLI::ValidationError`
possibly) if the value is not valid.
The two parameter template overload can be used in cases where you want to
restrict the input such as
```cpp
double val
app.add_option<double,unsigned int>("-v",val);
```
which would first verify the input is convertible to an `unsigned int` before
assigning it. Or using some variant type
```cpp
using vtype=std::variant<int, double, std::string>;
vtype v1;
app.add_option<vtype,std:string>("--vs",v1);
app.add_option<vtype,int>("--vi",v1);
app.add_option<vtype,double>("--vf",v1);
```
otherwise the output would default to a string. The `add_option` can be used
with any integral or floating point types, enumerations, or strings. Or any type
that takes an int, double, or std\::string in an assignment operator or
constructor. If an object can take multiple varieties of those, std::string
takes precedence, then double then int. To better control which one is used or
to use another type for the underlying conversions use the two parameter
template to directly specify the conversion type.
Types such as (std or boost) `optional<int>`, `optional<double>`, and
`optional<string>` and any other wrapper types are supported directly. For
purposes of CLI11 wrapper types are those which `value_type` definition. See
[CLI11 Advanced Topics/Custom Converters][] for information on how you can add your
own converters for additional types.
Vector types can also be used in the two parameter template overload
```cpp
std::vector<double> v1;
app.add_option<std::vector<double>,int>("--vs",v1);
```
would load a vector of doubles but ensure all values can be represented as
integers.
Automatic direct capture of the default string is disabled when using the two
parameter template. Use `set_default_str(...)` or
`->default_function(std::string())` to set the default string or capture
function directly for these cases.
Flag options specified through the `add_flag*` functions allow a syntax for the
option names to default particular options to a false value or any other value
if some flags are passed. For example:
```cpp
app.add_flag("--flag,!--no-flag",result,"help for flag");
```
specifies that if `--flag` is passed on the command line result will be true or
contain a value of 1. If `--no-flag` is passed `result` will contain false or -1
if `result` is a signed integer type, or 0 if it is an unsigned type. An
alternative form of the syntax is more explicit: `"--flag,--no-flag{false}"`;
this is equivalent to the previous example. This also works for short form
options `"-f,!-n"` or `"-f,-n{false}"`. If `variable_to_bind_to` is anything but
an integer value the default behavior is to take the last value given, while if
`variable_to_bind_to` is an integer type the behavior will be to sum all the
given arguments and return the result. This can be modified if needed by
changing the `multi_option_policy` on each flag (this is not inherited). The
default value can be any value. For example if you wished to define a numerical
flag:
```cpp
app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag")
```
Using any of those flags on the command line will result in the specified number
in the output. Similar things can be done for string values, and enumerations,
as long as the default value can be converted to the given type.
On a `C++14` compiler, you can pass a callback function directly to `.add_flag`,
while in C++11 mode you'll need to use `.add_flag_function` if you want a
callback function. The function will be given the number of times the flag was
passed. You can throw a relevant `CLI::ParseError` to signal a failure.
#### Example
- `"one,-o,--one"`: Valid as long as not a flag, would create an option that can
be specified positionally, or with `-o` or `--one`
- `"this"` Can only be passed positionally
- `"-a,-b,-c"` No limit to the number of non-positional option names
The add commands return a pointer to an internally stored `Option`. This option
can be used directly to check for the count (`->count()`) after parsing to avoid
a string based lookup.
#### Option options
Before parsing, you can set the following options:
- `->required()`: The program will quit if this option is not present. This is
`mandatory` in Plumbum, but required options seems to be a more standard term.
For compatibility, `->mandatory()` also works.
- `->expected(N)`: Take `N` values instead of as many as possible, only for
vector args. If negative, require at least `-N`; end with `--` or another
recognized option or subcommand.
- `->expected(MIN,MAX)`: Set a range of expected values to accompany an option.
`expected(0,1)` is the equivalent of making a flag.
- `->type_name(typename)`: Set the name of an Option's type (`type_name_fn`
allows a function instead)
- `->type_size(N)`: Set the intrinsic size of an option value. The parser will
require multiples of this number if negative. Most of the time this is
detected automatically though can be modified for specific use cases.
- `->type_size(MIN,MAX)`: Set the intrinsic size of an option to a range.
- `->needs(opt)`: This option requires another option to also be present, opt is
an `Option` pointer. Options can be removed from the `needs` with
`remove_needs(opt)`. The option can also be specified with a string containing
the name of the option
- `->excludes(opt)`: This option cannot be given with `opt` present, opt is an
`Option` pointer. Can also be given as a string containing the name of the
option. Options can be removed from the excludes list with
`->remove_excludes(opt)`
- `->envname(name)`: Gets the value from the environment if present and not
passed on the command line. π The value must also pass any validators to be
used.
- `->group(name)`: The help group to put the option in. No effect for positional
options. Defaults to `"Options"`. Options given an empty string will not show
up in the help print (hidden).
- `->ignore_case()`: Ignore the case on the command line (also works on
subcommands, does not affect arguments).
- `->ignore_underscore()`: Ignore any underscores in the options names (also
works on subcommands, does not affect arguments). For example "option_one"
will match with "optionone". This does not apply to short form options since
they only have one character
- `->disable_flag_override()`: From the command line long form flag options can
be assigned a value on the command line using the `=` notation `--flag=value`.
If this behavior is not desired, the `disable_flag_override()` disables it and
will generate an exception if it is done on the command line. The `=` does not
work with short form flag options.
- `->allow_extra_args(true/false)`: If set to true the option will take an
unlimited number of arguments like a vector, if false it will limit the number
of arguments to the size of the type used in the option. Default value depends
on the nature of the type use, containers default to true, others default to
false.
- `->delimiter(char)`: Allows specification of a custom delimiter for separating
single arguments into vector arguments, for example specifying
`->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3
elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a
vector value.
- `->description(str)`: Set/change the description.
- `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option
policy. Shortcuts available: `->take_last()`, `->take_first()`,`->take_all()`,
and `->join()`. This will only affect options expecting 1 argument or bool
flags (which do not inherit their default but always start with a specific
policy). `->join(delim)` can also be used to join with a specific delimiter.
This equivalent to calling `->delimiter(delim)` and `->join()`. Valid values
are `CLI::MultiOptionPolicy::Throw`, `CLI::MultiOptionPolicy::Throw`,
`CLI::MultiOptionPolicy::TakeLast`, `CLI::MultiOptionPolicy::TakeFirst`,
`CLI::MultiOptionPolicy::Join`, `CLI::MultiOptionPolicy::TakeAll`,
`CLI::MultiOptionPolicy::Sum`, and `CLI::MultiOptionPolicy::Reverse` π.
- `->check(std::string(const std::string &), validator_name="",validator_description="")`:
Define a check function. The function should return a non empty string with
the error message if the check fails
- `->check(Validator)`: Use a Validator object to do the check see
[Validators](#validators) for a description of available Validators and how to
create new ones.
- `->transform(std::string(std::string &), validator_name="",validator_description=")`:
Converts the input string into the output string, in-place in the parsed
options.
- `->transform(Validator)`: Uses a Validator object to do the transformation see
[Validators](#validators) for a description of available Validators and how to
create new ones.
- `->each(void(const std::string &)>`: Run this function on each value received,
as it is received. It should throw a `ValidationError` if an error is
encountered.
- `->configurable(false)`: Disable this option from being in a configuration
file.
- `->capture_default_str()`: Store the current value attached and display it in
the help string.
- `->default_function(std::string())`: Advanced: Change the function that
`capture_default_str()` uses.
- `->always_capture_default()`: Always run `capture_default_str()` when creating
new options. Only useful on an App's `option_defaults`.
- `->default_str(string)`: Set the default string directly (NO VALIDATION OR
CALLBACKS). This string will also be used as a default value if no arguments
are passed and the value is requested.
- `->default_val(value)`: Generate the default string from a value and validate
that the value is also valid. For options that assign directly to a value type
the value in that type is also updated. Value must be convertible to a
string(one of known types or have a stream operator). The callback may be
triggered if the `run_callback_for_default` is set.
- `->run_callback_for_default()`: This will force the option callback to be
executed or the variable set when the `default_val` is set.
- `->option_text(string)`: Sets the text between the option name and
description.
- `->force_callback()`: Causes the option callback or value set to be triggered
even if the option was not present in parsing.
- `->trigger_on_parse()`: If set, causes the callback and all associated
validation checks for the option to be executed when the option value is
parsed vs. at the end of all parsing. This could cause the callback to be
executed multiple times. Also works with positional options.
These options return the `Option` pointer, so you can chain them together, and
even skip storing the pointer entirely. The `each` function takes any function
that has the signature `void(const std::string&)`; it should throw a
`ValidationError` when validation fails. The help message will have the name of
the parent option prepended. Since `each`, `check` and `transform` use the same
underlying mechanism, you can chain as many as you want, and they will be
executed in order. Operations added through `transform` are executed first in
reverse order of addition, and `check` and `each` are run following the
transform functions in order of addition. If you just want to see the
unconverted values, use `.results()` to get the `std::vector<std::string>` of
results.
On the command line, options can be given as:
- `-a` (flag)
- `-abc` (flags can be combined)
- `-f filename` (option)
- `-ffilename` (no space required)
- `-abcf filename` (flags and option can be combined)
- `--long` (long flag)
- `--long_flag=true` (long flag with equals to override default value)
- `--file filename` (space)
- `--file=filename` (equals)
If `allow_windows_style_options()` is specified in the application or subcommand
options can also be given as:
- `/a` (flag)
- `/f filename` (option)
- `/long` (long flag)
- `/file filename` (space)
- `/file:filename` (colon)
- `/long_flag:false` (long flag with : to override the default value)
- Windows style options do not allow combining short options or values not
separated from the short option like with `-` options
Long flag options may be given with an `=<value>` to allow specifying a false
value, or some other value to the flag. See [config files](#configuration-file)
for details on the values supported. NOTE: only the `=` or `:` for windows-style
options may be used for this, using a space will result in the argument being
interpreted as a positional argument. This syntax can override the default
values, and can be disabled by using `disable_flag_override()`.
Extra positional arguments will cause the program to exit, so at least one
positional option with a vector is recommended if you want to allow extraneous
arguments. If you set `.allow_extras()` on the main `App`, you will not get an
error. You can access the missing options using `remaining` (if you have
subcommands, `app.remaining(true)` will get all remaining options, subcommands
included). If the remaining arguments are to processed by another `App` then the
function `remaining_for_passthrough()` can be used to get the remaining
arguments in reverse order such that `app.parse(vector)` works directly and
could even be used inside a subcommand callback.
You can access a vector of pointers to the parsed options in the original order
using `parse_order()`. If `--` is present in the command line that does not end
an unlimited option, then everything after that is positional only.
#### Validators
Validators are structures to check or modify inputs, they can be used to verify
that an input meets certain criteria or transform it into another value. They
are added through the `check` or `transform` functions. The differences between
the two function are that checks do not modify the input whereas transforms can
and are executed before any Validators added through `check`.
CLI11 has several Validators built-in that perform some common checks
- `CLI::IsMember(...)`: Require an option be a member of a given set. See
[Transforming Validators](#transforming-validators) for more details.
- `CLI::Transformer(...)`: Modify the input using a map. See
[Transforming Validators](#transforming-validators) for more details.
- `CLI::CheckedTransformer(...)`: Modify the input using a map, and require that
the input is either in the set or already one of the outputs of the set. See
[Transforming Validators](#transforming-validators) for more details.
- `CLI::AsNumberWithUnit(...)`: Modify the `<NUMBER> <UNIT>` pair by matching
the unit and multiplying the number by the corresponding factor. It can be
used as a base for transformers, that accept things like size values (`1 KB`)
or durations (`0.33 ms`).
- `CLI::AsSizeValue(...)`: Convert inputs like `100b`, `42 KB`, `101 Mb`,
`11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3
or 2^10.
- `CLI::ExistingFile`: Requires that the file exists if given.
- `CLI::ExistingDirectory`: Requires that the directory exists.
- `CLI::ExistingPath`: Requires that the path (file or directory) exists.
- `CLI::NonexistentPath`: Requires that the path does not exist.
- `CLI::FileOnDefaultPath`: Best used as a transform, Will check that a file
exists either directly or in a default path and update the path appropriately.
See [Transforming Validators](#transforming-validators) for more details
- `CLI::Range(min,max)`: Requires that the option be between min and max (make
sure to use floating point if needed). Min defaults to 0.
- `CLI::Bounded(min,max)`: Modify the input such that it is always between min
and max (make sure to use floating point if needed). Min defaults to 0. Will
produce an error if conversion is not possible.
- `CLI::PositiveNumber`: Requires the number be greater than 0
- `CLI::NonNegativeNumber`: Requires the number be greater or equal to 0
- `CLI::Number`: Requires the input be a number.
- `CLI::ValidIPV4`: Requires that the option be a valid IPv4 string e.g.
`'255.255.255.255'`, `'10.1.1.7'`.
- `CLI::TypeValidator<TYPE>`:Requires that the option be convertible to the
specified type e.g. `CLI::TypeValidator<unsigned int>()` would require that
the input be convertible to an `unsigned int` regardless of the end
conversion.
These Validators can be used by simply passing the name into the `check` or
`transform` methods on an option
```cpp
->check(CLI::ExistingFile);
->check(CLI::Range(0,10));
```
Validators can be merged using `&` and `|` and inverted using `!`. For example:
```cpp
->check(CLI::Range(0,10)|CLI::Range(20,30));
```
will produce a check to ensure a value is between 0 and 10 or 20 and 30.
```cpp
->check(!CLI::PositiveNumber);
```
will produce a check for a number less than or equal to 0.
##### Transforming Validators
There are a few built in Validators that let you transform values if used with
the `transform` function. If they also do some checks then they can be used
`check` but some may do nothing in that case.
- `CLI::Bounded(min,max)` will bound values between min and max and values
outside of that range are limited to min or max, it will fail if the value
cannot be converted and produce a `ValidationError`
- The `IsMember` Validator lets you specify a set of predefined options. You can
pass any container or copyable pointer (including `std::shared_ptr`) to a
container to this Validator; the container just needs to be iterable and have
a `::value_type`. The key type should be convertible from a string, You can
use an initializer list directly if you like. If you need to modify the set
later, the pointer form lets you do that; the type message and check will
correctly refer to the current version of the set. The container passed in can
be a set, vector, or a map like structure. If used in the `transform` method
the output value will be the matching key as it could be modified by filters.
After specifying a set of options, you can also specify "filter" functions of
the form `T(T)`, where `T` is the type of the values. The most common choices
probably will be `CLI::ignore_case` an `CLI::ignore_underscore`, and
`CLI::ignore_space`. These all work on strings but it is possible to define
functions that work on other types. Here are some examples of `IsMember`:
- `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
- `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`:
Match things like `Choice_1`, too.
- `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you
just need `std::begin`, `std::end`, and `::value_type`.
- `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You
can use maps; in `->transform()` these replace the matched value with the
matched key. The value member of the map is not used in `IsMember`, so it can
be any type.
- `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`:
You can modify `p` later.
- The `Transformer` and `CheckedTransformer` Validators transform one value into
another. Any container or copyable pointer (including `std::shared_ptr`) to a
container that generates pairs of values can be passed to these `Validator's`;
the container just needs to be iterable and have a `::value_type` that
consists of pairs. The key type should be convertible from a string, and the
value type should be convertible to a string You can use an initializer list
directly if you like. If you need to modify the map later, the pointer form
lets you do that; the description message will correctly refer to the current
version of the map. `Transformer` does not do any checking so values not in
the map are ignored. `CheckedTransformer` takes an extra step of verifying
that the value is either one of the map key values, in which case it is
transformed, or one of the expected output values, and if not will generate a
`ValidationError`. A Transformer placed using `check` will not do anything.
After specifying a map of options, you can also specify "filter" just like in
`CLI::IsMember`. Here are some examples (`Transformer` and `CheckedTransformer`
are interchangeable in the examples) of `Transformer`:
- `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values
and produce map values.
- `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`:
most maplike containers work, the `::value_type` needs to produce a pair of
some kind.
- `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`:
You can use maps; in `->transform()` these replace the matched key with the
value. `CheckedTransformer` also requires that the value either match one of
the keys or match one of known outputs.
- `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`:
You can modify `p` later. `TransformPairs<T>` is an alias for
`std::vector<std::pair<<std::string,T>>`
NOTES: If the container used in `IsMember`, `Transformer`, or
`CheckedTransformer` has a `find` function like `std::unordered_map` or
`std::map` then that function is used to do the searching. If it does not have a
`find` function a linear search is performed. If there are filters present, the
fast search is performed first, and if that fails a linear search with the
filters on the key values is performed.
- `CLI::FileOnDefaultPath(default_path)`: can be used to check for files in a
default path. If used as a transform it will first check that a file exists,
if it does nothing further is done, if it does not it tries to add a default
Path to the file and search there again. If the file does not exist an error
is returned normally but this can be disabled using
`CLI::FileOnDefaultPath(default_path, false)`. This allows multiple paths to
be chained using multiple transform calls.
- `CLI::EscapedString`: π can be used to process an escaped string. The
processing is equivalent to that used for TOML config files, see
[TOML strings](https://toml.io/en/v1.0.0#string). With 2 notable exceptions.
\` can also be used as a literal string notation, and it also allows binary
string notation see
[binary strings](https://cliutils.github.io/CLI11/book/chapters/config.html).
The escaped string processing will remove outer quotes if present, `"` will
indicate a string with potential escape sequences, `'` and \` will indicate a
literal string and the quotes removed but no escape sequences will be
processed. This is the same escape processing as used in config files.
##### Validator operations
Validators are copyable and have a few operations that can be performed on them
to alter settings. Most of the built in Validators have a default description
that is displayed in the help. This can be altered via
`.description(validator_description)`. The name of a Validator, which is useful
for later reference from the `get_validator(name)` method of an `Option` can be
set via `.name(validator_name)` The operation function of a Validator can be set
via `.operation(std::function<std::string(std::string &>)`. The `.active()`
function can activate or deactivate a Validator from the operation. A validator
can be set to apply only to a specific element of the output. For example in a
pair option `std::pair<int, std::string>` the first element may need to be a
positive integer while the second may need to be a valid file. The
`.application_index(int)` function can specify this. It is zero based and
negative indices apply to all values.
```cpp
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
```
All the validator operation functions return a Validator reference allowing them
to be chained. For example
```cpp
opt->check(CLI::Range(10,20).description("range is limited to sensible values").active(false).name("range"));
```
will specify a check on an option with a name "range", but deactivate it for the
time being. The check can later be activated through
```cpp
opt->get_validator("range")->active();
```
##### Custom Validators
A validator object with a custom function can be created via
```cpp
CLI::Validator(std::function<std::string(std::string &)>,validator_description,validator_name="");
```
or if the operation function is set later they can be created with
```cpp
CLI::Validator(validator_description);
```
It is also possible to create a subclass of `CLI::Validator`, in which case it
can also set a custom description function, and operation function.
##### Querying Validators
Once loaded into an Option, a pointer to a named Validator can be retrieved via
```cpp
opt->get_validator(name);
```
This will retrieve a Validator with the given name or throw a
`CLI::OptionNotFound` error. If no name is given or name is empty the first
unnamed Validator will be returned or the first Validator if there is only one.
or
```cpp
opt->get_validator(index);
```
Which will return a validator in the index it is applied which isn't necessarily
the order in which was defined. The pointer can be `nullptr` if an invalid index
is given. Validators have a few functions to query the current values:
- `get_description()`: Will return a description string
- `get_name()`: Will return the Validator name
- `get_active()`: Will return the current active state, true if the Validator is
active.
- `get_application_index()`: Will return the current application index.
- `get_modifying()`: Will return true if the Validator is allowed to modify the
input, this can be controlled via the `non_modifying()` method, though it is
recommended to let `check` and `transform` option methods manipulate it if
needed.
#### Getting results
In most cases, the fastest and easiest way is to return the results through a
callback or variable specified in one of the `add_*` functions. But there are
situations where this is not possible or desired. For these cases the results
may be obtained through one of the following functions. Please note that these
functions will do any type conversions and processing during the call so should
not used in performance critical code:
- `->results()`: Retrieves a vector of strings with all the results in the order
they were given.
- `->results(variable_to_bind_to)`: Gets the results according to the
MultiOptionPolicy and converts them just like the `add_option_function` with a
variable.
- `Value=opt->as<type>()`: Returns the result or default value directly as the
specified type if possible, can be vector to return all results, and a
non-vector to get the result according to the MultiOptionPolicy in place.
### Subcommands
Subcommands are keywords that invoke a new set of options and features. For
example, the `git` command has a long series of subcommands, like `add` and
`commit`. Each can have its own options and implementations. Subcommands are
supported in CLI11, and can be nested infinitely. To add a subcommand, call the
`add_subcommand` method with a name and an optional description. This gives a
pointer to an `App` that behaves just like the main app, and can take options or
further subcommands. Add `->ignore_case()` to a subcommand to allow any
variation of caps to also be accepted. `->ignore_underscore()` is similar, but
for underscores. Children inherit the current setting from the parent. You
cannot add multiple matching subcommand names at the same level (including
`ignore_case` and `ignore_underscore`).
If you want to require that at least one subcommand is given, use
`.require_subcommand()` on the parent app. You can optionally give an exact
number of subcommands to require, as well. If you give two arguments, that sets
the min and max number allowed. 0 for the max number allowed will allow an
unlimited number of subcommands. As a handy shortcut, a single negative value N
will set "up to N" values. Limiting the maximum number allows you to keep
arguments that match a previous subcommand name from matching.
If an `App` (main or subcommand) has been parsed on the command line, `->parsed`
will be true (or convert directly to bool). All `App`s have a
`get_subcommands()` method, which returns a list of pointers to the subcommands
passed on the command line. A `got_subcommand(App_or_name)` method is also
provided that will check to see if an `App` pointer or a string name was
collected on the command line.
For many cases, however, using an app's callback capabilities may be easier.
Every app has a set of callbacks that can be executed at various stages of
parsing; a `C++` lambda function (with capture to get parsed values) can be used
as input to the callback definition function. If you throw `CLI::Success` or
`CLI::RuntimeError(return_value)`, you can even exit the program through the
callback.
Multiple subcommands are allowed, to allow [`Click`][click] like series of
commands (order is preserved). The same subcommand can be triggered multiple
times but all positional arguments will take precedence over the second and
future calls of the subcommand. `->count()` on the subcommand will return the
number of times the subcommand was called. The subcommand callback will only be
triggered once unless the `.immediate_callback()` flag is set or the callback is
specified through the `parse_complete_callback()` function. The
`final_callback()` is triggered only once. In which case the callback executes
on completion of the subcommand arguments but after the arguments for that
subcommand have been parsed, and can be triggered multiple times.
Subcommands may also have an empty name either by calling `add_subcommand` with
an empty string for the name or with no arguments. Nameless subcommands function
a similarly to groups in the main `App`. See [Option groups](#option-groups) to
see how this might work. If an option is not defined in the main App, all
nameless subcommands are checked as well. This allows for the options to be
defined in a composable group. The `add_subcommand` function has an overload for
adding a `shared_ptr<App>` so the subcommand(s) could be defined in different
components and merged into a main `App`, or possibly multiple `Apps`. Multiple
nameless subcommands are allowed. Callbacks for nameless subcommands are only
triggered if any options from the subcommand were parsed. Subcommand names given
through the `add_subcommand` method have the same restrictions as option names.
π Options or flags in a subcommand may be directly specified using dot notation
- `--subcommand.long=val` (long subcommand option)
- `--subcommand.long val` (long subcommand option)
- `--subcommand.f=val` (short form subcommand option)
- `--subcommand.f val` (short form subcommand option)
- `--subcommand.f` (short form subcommand flag)
- `--subcommand1.subsub.f val` (short form nested subcommand option)
The use of dot notation in this form is equivalent `--subcommand.long <args>` =>
`subcommand --long <args> ++`. Nested subcommands also work `sub1.subsub` would
trigger the subsub subcommand in `sub1`. This is equivalent to "sub1 subsub".
Quotes around the subcommand names are permitted π following the TOML standard
for such specification. This includes allowing escape sequences. For example
`"subcommand".'f'` or `"subcommand.with.dots".arg1 = value`.
#### Subcommand options
There are several options that are supported on the main app and subcommands and
option_groups. These are:
- `.ignore_case()`: Ignore the case of this subcommand. Inherited by added
subcommands, so is usually used on the main `App`.
- `.ignore_underscore()`: Ignore any underscores in the subcommand name.
Inherited by added subcommands, so is usually used on the main `App`.
- `.allow_windows_style_options()`: Allow command line options to be parsed in
the form of `/s /long /file:file_name.ext` This option does not change how
options are specified in the `add_option` calls or the ability to process
options in the form of `-s --long --file=file_name.ext`.
- `.allow_non_standard_option_names()`:π§ Allow specification of single `-` long
form option names. This is not recommended but is available to enable
reworking of existing interfaces. If this modifier is enabled on an app or
subcommand, options or flags can be specified like normal but instead of
throwing an exception, long form single dash option names will be allowed. It
is not allowed to have a single character short option starting with the same
character as a single dash long form name; for example, `-s` and `-single` are
not allowed in the same application.
- `.fallthrough()`: Allow extra unmatched options and positionals to "fall
through" and be matched on a parent option. Subcommands by default are allowed
to "fall through" as in they will first attempt to match on the current
subcommand and if they fail will progressively check parents for matching
subcommands. This can be disabled through `subcommand_fallthrough(false)` π§.
- `.subcommand_fallthrough()`: π§ Allow subcommands to "fall through" and be
matched on a parent option. Disabling this prevents additional subcommands at
the same level from being matched. It can be useful in certain circumstances
where there might be ambiguity between subcommands and positionals. The
default is true.
- `.configurable()`: Allow the subcommand to be triggered from a configuration
file. By default subcommand options in a configuration file do not trigger a
subcommand but will just update default values.
- `.disable()`: Specify that the subcommand is disabled, if given with a bool
value it will enable or disable the subcommand or option group.
- `.disabled_by_default()`: Specify that at the start of parsing the
subcommand/option_group should be disabled. This is useful for allowing some
Subcommands to trigger others.
- `.enabled_by_default()`: Specify that at the start of each parse the
subcommand/option_group should be enabled. This is useful for allowing some
Subcommands to disable others.
- `.silent()`: Specify that the subcommand is silent meaning that if used it
won't show up in the subcommand list. This allows the use of subcommands as
modifiers
- `.validate_positionals()`: Specify that positionals should pass validation
before matching. Validation is specified through `transform`, `check`, and
`each` for an option. If an argument fails validation it is not an error and
matching proceeds to the next available positional or extra arguments.
- `.validate_optional_arguments()`: Specify that optional arguments should pass
validation before being assigned to an option. Validation is specified through
`transform`, `check`, and `each` for an option. If an argument fails
validation it is not an error and matching proceeds to the next available
positional subcommand or extra arguments.
- `.excludes(option_or_subcommand)`: If given an option pointer or pointer to
another subcommand, these subcommands cannot be given together. In the case of
options, if the option is passed the subcommand cannot be used and will
generate an error.
- `.needs(option_or_subcommand)`: If given an option pointer or pointer to
another subcommand, the subcommands will require the given option to have been
given before this subcommand is validated which occurs prior to execution of
any callback or after parsing is completed.
- `.require_option()`: Require 1 or more options or option groups be used.
- `.require_option(N)`: Require `N` options or option groups, if `N>0`, or up to
`N` if `N<0`. `N=0` resets to the default to 0 or more.
- `.require_option(min, max)`: Explicitly set min and max allowed options or
option groups. Setting `max` to 0 implies unlimited options.
- `.require_subcommand()`: Require 1 or more subcommands.
- `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if
`N<0`. `N=0` resets to the default to 0 or more.
- `.require_subcommand(min, max)`: Explicitly set min and max allowed
subcommands. Setting `max` to 0 is unlimited.
- `.add_subcommand(name="", description="")`: Add a subcommand, returns a
pointer to the internally stored subcommand.
- `.add_subcommand(shared_ptr<App>)`: Add a subcommand by shared_ptr, returns a
pointer to the internally stored subcommand.
- `.remove_subcommand(App)`: Remove a subcommand from the app or subcommand.
- `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on
the command line.
- `.get_subcommands(filter)`: The list of subcommands that match a particular
filter function.
- `.add_option_group(name="", description="")`: Add an
[option group](#option-groups) to an App, an option group is specialized
subcommand intended for containing groups of options or other groups for
controlling how options interact.
- `.get_parent()`: Get the parent App or `nullptr` if called on main App.
- `.get_option(name)`: Get an option pointer by option name will throw if the
specified option is not available, nameless subcommands are also searched
- `.get_option_no_throw(name)`: Get an option pointer by option name. This
function will return a `nullptr` instead of throwing if the option is not
available.
- `.get_options(filter)`: Get the list of all defined option pointers (useful
for processing the app for custom output formats).
- `.parse_order()`: Get the list of option pointers in the order they were
parsed (including duplicates).
- `.formatter(fmt)`: Set a formatter, with signature
`std::string(const App*, std::string, AppFormatMode)`. See Formatting for more
details.
- `.description(str)`: Set/change the description.
- `.get_description()`: Access the description.
- `.alias(str)`: set an alias for the subcommand, this allows subcommands to be
called by more than one name.
- `.parsed()`: True if this subcommand was given on the command line.
- `.count()`: Returns the number of times the subcommand was called.
- `.count(option_name)`: Returns the number of times a particular option was
called.
- `.count_all()`: Returns the total number of arguments a particular subcommand
processed, on the main App it returns the total number of processed commands.
- `.name(name)`: Add or change the name.
- `.callback(void() function)`: Set the callback for an app. Either sets the
`pre_parse_callback` or the `final_callback` depending on the value of
`immediate_callback`. See [Subcommand callbacks](#callbacks) for some
additional details.
- `.parse_complete_callback(void() function)`: Set the callback that runs at the
completion of parsing. For subcommands this is executed at the completion of
the single subcommand and can be executed multiple times. See
[Subcommand callbacks](#callbacks) for some additional details.
- `.final_callback(void() function)`: Set the callback that runs at the end of
all processing. This is the last thing that is executed before returning. See
[Subcommand callbacks](#callbacks) for some additional details.
- `.immediate_callback()`: Specifies whether the callback for a subcommand
should be run as a `parse_complete_callback`(true) or `final_callback`(false).
When used on the main app it will execute the main app callback prior to the
callbacks for a subcommand if they do not also have the `immediate_callback`
flag set. It is preferable to use the `parse_complete_callback` or
`final_callback` directly instead of the `callback` and `immediate_callback`
if one wishes to control the ordering and timing of callback. Though
`immediate_callback` can be used to swap them if that is needed.
- `.pre_parse_callback(void(std::size_t) function)`: Set a callback that
executes after the first argument of an application is processed. See
[Subcommand callbacks](#callbacks) for some additional details.
- `.allow_extras()`: Do not throw an error if extra arguments are left over.
- `.positionals_at_end()`: Specify that positional arguments occur as the last
arguments and throw an error if an unexpected positional is encountered.
- `.prefix_command()`: Like `allow_extras`, but stop processing immediately on
the first unrecognized item. All subsequent arguments are placed in the
remaining_arg list. It is ideal for allowing your app or subcommand to be a
"prefix" to calling another app.
- `.usage(message)`: π Replace text to appear at the start of the help string
after description.
- `.usage(std::string())`: π Set a callback to generate a string that will
appear at the start of the help string after description.
- `.footer(message)`: Set text to appear at the bottom of the help string.
- `.footer(std::string())`: Set a callback to generate a string that will appear
at the end of the help string.
- `.set_help_flag(name, message)`: Set the help flag name and message, returns a
pointer to the created option.
- `.set_version_flag(name, versionString or callback, help_message)`: Set the
version flag name and version string or callback and optional help message,
returns a pointer to the created option.
- `.set_help_all_flag(name, message)`: Set the help all flag name and message,
returns a pointer to the created option. Expands subcommands.
- `.failure_message(func)`: Set the failure message function. Two provided:
`CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
- `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting an
empty string for the name will be hide the subcommand.
- `[option_name]`: retrieve a const pointer to an option given by `option_name`
for Example `app["--flag1"]` will get a pointer to the option for the
"--flag1" value, `app["--flag1"]->as<bool>()` will get the results of the
command line for a flag. The operation will throw an exception if the option
name is not valid.
> [!NOTE]
>
> If you have a fixed number of required positional options, that will match
> before subcommand names. `{}` is an empty filter function, and any positional
> argument will match before repeated subcommand names.
#### Callbacks
A subcommand has three optional callbacks that are executed at different stages
of processing. The `preparse_callback` is executed once after the first argument
of a subcommand or application is processed and gives an argument for the number
of remaining arguments to process. For the main app the first argument is
considered the program name, for subcommands the first argument is the
subcommand name. For Option groups and nameless subcommands the first argument
is after the first argument or subcommand is processed from that group. The
second callback is executed after parsing. This is known as the
`parse_complete_callback`. For subcommands this is executed immediately after
parsing and can be executed multiple times if a subcommand is called multiple
times. On the main app this callback is executed after all the
`parse_complete_callback`s for the subcommands are executed but prior to any
`final_callback` calls in the subcommand or option groups. If the main app or
subcommand has a config file, no data from the config file will be reflected in
`parse_complete_callback` on named subcommands. For `option_group`s the
`parse_complete_callback` is executed prior to the `parse_complete_callback` on
the main app but after the `config_file` is loaded (if specified). The
`final_callback` is executed after all processing is complete. After the
`parse_complete_callback` is executed on the main app, the used subcommand
`final_callback` are executed followed by the "final callback" for option
groups. The last thing to execute is the `final_callback` for the `main_app`.
For example say an application was set up like
```cpp
app.parse_complete_callback(ac1);
app.final_callback(ac2);
auto sub1=app.add_subcommand("sub1")->parse_complete_callback(c1)->preparse_callback(pc1);
auto sub2=app.add_subcommand("sub2")->final_callback(c2)->preparse_callback(pc2);
app.preparse_callback( pa);
... A bunch of other options
```
Then the command line is given as
```bash
program --opt1 opt1_val sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val
```
- `pa` will be called prior to parsing any values with an argument of 13.
- `pc1` will be called immediately after processing the `sub1` command with a
value of 10.
- `c1` will be called when the `sub2` command is encountered.
- `pc2` will be called with value of 6 after the `sub2` command is encountered.
- `c1` will be called again after the second `sub2` command is encountered.
- `ac1` will be called after processing of all arguments
- `c2` will be called once after processing all arguments.
- `ac2` will be called last after completing all lower level callbacks have been
executed.
A subcommand is considered terminated when one of the following conditions are
met.
1. There are no more arguments to process
2. Another subcommand is encountered that would not fit in an optional slot of
the subcommand
3. The `positional_mark` (`--`) is encountered and there are no available
positional slots in the subcommand.
4. The `subcommand_terminator` mark (`++`) is encountered
Prior to executed a `parse_complete_callback` all contained options are
processed before the callback is triggered. If a subcommand with a
`parse_complete_callback` is called again, then the contained options are reset,
and can be triggered again.
#### Option groups
The subcommand method
```cpp
.add_option_group(name,description)
```
Will create an option group, and return a pointer to it. The argument for
`description` is optional and can be omitted. An option group allows creation of
a collection of options, similar to the groups function on options, but with
additional controls and requirements. They allow specific sets of options to be
composed and controlled as a collective. For an example see
[range example](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp).
Option groups are a specialization of an App so all
[functions](#subcommand-options) that work with an App or subcommand also work
on option groups. Options can be created as part of an option group using the
add functions just like a subcommand, or previously created options can be added
through. The name given in an option group must not contain newlines or null
characters.
```cpp
ogroup->add_option(option_pointer);
ogroup->add_options(option_pointer);
ogroup->add_options(option1,option2,option3,...);
```
The option pointers used in this function must be options defined in the parent
application of the option group otherwise an error will be generated.
Subcommands can also be added via
```cpp
ogroup->add_subcommand(subcom_pointer);
```
This results in the subcommand being moved from its parent into the option
group.
Options in an option group are searched for a command line match after any
options in the main app, so any positionals in the main app would be matched
first. So care must be taken to make sure of the order when using positional
arguments and option groups. Option groups work well with `excludes` and
`require_options` methods, as an application will treat an option group as a
single option for the purpose of counting and requirements, and an option group
will be considered used if any of the options or subcommands contained in it are
used. Option groups allow specifying requirements such as requiring 1 of 3
options in one group and 1 of 3 options in a different group. Option groups can
contain other groups as well. Disabling an option group will turn off all
options within the group.
The `CLI::TriggerOn` and `CLI::TriggerOff` methods are helper functions to allow
the use of options/subcommands from one group to trigger another group on or
off.
```cpp
CLI::TriggerOn(group1_pointer, triggered_group);
CLI::TriggerOff(group2_pointer, disabled_group);
```
These functions make use of `preparse_callback`, `enabled_by_default()` and
`disabled_by_default`. The triggered group may be a vector of group pointers.
These methods should only be used once per group and will override any previous
use of the underlying functions. More complex arrangements can be accomplished
using similar methodology with a custom `preparse_callback` function that does
more.
Additional helper functions `deprecate_option` and `retire_option` are available
to deprecate or retire options
```cpp
CLI::deprecate_option(option *, replacement_name="");
CLI::deprecate_option(App,option_name,replacement_name="");
```
will specify that the option is deprecated which will display a message in the
help and a warning on first usage. Deprecated options function normally but will
add a message in the help and display a warning on first use.
```cpp
CLI::retire_option(App,option *);
CLI::retire_option(App,option_name);
```
will create an option that does nothing by default and will display a warning on
first usage that the option is retired and has no effect. If the option exists
it is replaces with a dummy option that takes the same arguments.
If an empty string is passed the option group name the entire group will be
hidden in the help results. For example.
```cpp
auto hidden_group=app.add_option_group("");
```
will create a group such that no options in that group are displayed in the help
string. For the purposes of help display, if the option group name starts with a
'+' it is treated as if it were not in a group for help and get_options. For
example:
```cpp
auto added_group=app.add_option_group("+sub");
```
In this case the help output will not reference the option group and options
inside of it will be treated for most purposes as if they were part of the
parent.
### Configuration file
```cpp
app.set_config(option_name="",
default_file_name="",
help_string="Read an ini file",
required=false)
```
If this is called with no arguments, it will remove the configuration file
option (like `set_help_flag`). Setting a configuration option is special. If it
is present, it will be read along with the normal command line arguments. The
file will be read if it exists, and does not throw an error unless `required` is
`true`. Configuration files are in [TOML][] format by default, though the
default reader can also accept files in INI format as well. The config reader
can read most aspects of TOML files including strings both literal π and with
potential escape sequences π, digit separators π, and multi-line strings π,
and run them through the CLI11 parser. Other formats can be added by an adept
user, some variations are available through customization points in the default
formatter. An example of a TOML file:
```toml
# Comments are supported, using a #
# The default section is [default], case insensitive
value = 1
value2 = 123_456 # a string with separators
str = "A string"
str2 = "A string\nwith new lines"
str3 = 'A literal "string"'
vector = [1,2,3]
str_vector = ["one","two","and three"]
# Sections map to subcommands
[subcommand]
in_subcommand = Wow
sub.subcommand = true
"sub"."subcommand2" = "string_value"
```
or equivalently in INI format
```ini
; Comments are supported, using a ;
; The default section is [default], case insensitive
value = 1
str = "A string"
vector = 1 2 3
str_vector = "one" "two" "and three"
; Sections map to subcommands
[subcommand]
in_subcommand = Wow
sub.subcommand = true
```
Spaces before and after the name and argument are ignored. Multiple arguments
are separated by spaces. One set of quotes will be removed, preserving spaces
(the same way the command line works). Boolean options can be `true`, `on`, `1`,
`yes`, `enable`; or `false`, `off`, `0`, `no`, `disable` (case insensitive).
Sections (and `.` separated names) are treated as subcommands (note: this does
not necessarily mean that subcommand was passed, it just sets the "defaults").
You cannot set positional-only arguments. Subcommands can be triggered from
configuration files if the `configurable` flag was set on the subcommand. Then
the use of `[subcommand]` notation will trigger a subcommand and cause it to act
as if it were on the command line.
To print a configuration file from the passed arguments, use
`.config_to_str(default_also=false, write_description=false)`, where
`default_also` will also show any defaulted arguments, and `write_description`
will include the app and option descriptions. See
[Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for
some additional details and customization points.
If it is desired that multiple configuration be allowed. Use
```cpp
app.set_config("--config")->expected(1, X);
```
Where X is some positive number and will allow up to `X` configuration files to
be specified by separate `--config` arguments. Value strings with quote
characters in it will be printed with a single quote. All other arguments will
use double quote. Empty strings will use a double quoted argument. Numerical or
boolean values are not quoted.
For options or flags which allow 0 arguments to be passed using an empty string
in the config file, `{}`, or `[]` will convert the result to the default value
specified via `default_str` or `default_val` on the option. If no user specified
default is given the result is an empty string or the converted value of an
empty string.
NOTE: Transforms and checks can be used with the option pointer returned from
set_config like any other option to validate the input if needed. It can also be
used with the built in transform `CLI::FileOnDefaultPath` to look in a default
path as well as the current one. For example
```cpp
app.set_config("--config")->transform(CLI::FileOnDefaultPath("/to/default/path/"));
```
See [Transforming Validators](#transforming-validators) for additional details
on this validator. Multiple transforms or validators can be used either by
multiple calls or using `|` operations with the transform.
### Inheriting defaults
Many of the defaults for subcommands and even options are inherited from their
creators. The inherited default values for subcommands are `allow_extras`,
`prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`,
`usage`, `footer`, `immediate_callback` and maximum number of required
subcommands. The help flag existence, name, and description are inherited, as
well.
Options have defaults for `group`, `required`, `multi_option_policy`,
`ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To
set these defaults, you should set the `option_defaults()` object, for example:
```cpp
app.option_defaults()->required();
// All future options will be required
```
The default settings for options are inherited to subcommands, as well.
### Formatting
The job of formatting help printouts is delegated to a formatter callable object
on Apps and Options. You are free to replace either formatter by calling
`formatter(fmt)` on an `App`, where fmt is any copyable callable with the
correct signature. CLI11 comes with a default App formatter functional,
`Formatter`. It is customizable; you can set `label(key, value)` to replace the
default labels like `REQUIRED`, and `column_width(n)` to set the width of the
columns before you add the functional to the app or option. You can also
override almost any stage of the formatting process in a subclass of either
formatter. If you want to make a new formatter from scratch, you can do that
too; you just need to implement the correct signature. The first argument is a
const pointer to the in question. The formatter will get a `std::string` usage
name as the second option, and a `AppFormatMode` mode for the final option. It
should return a `std::string`.
The `AppFormatMode` can be `Normal`, `All`, or `Sub`, and it indicates the
situation the help was called in. `Sub` is optional, but the default formatter
uses it to make sure expanded subcommands are called with their own formatter
since you can't access anything but the call operator once a formatter has been
set.
### Subclassing
The App class was designed allow toolkits to subclass it, to provide preset
default options (see above) and setup/teardown code. Subcommands remain an
unsubclassed `App`, since those are not expected to need setup and teardown. The
default `App` only adds a help flag, `-h,--help`, than can removed/replaced
using `.set_help_flag(name, help_string)`. You can also set a help-all flag with
`.set_help_all_flag(name, help_string)`; this will expand the subcommands (one
level only). You can remove options if you have pointers to them using
`.remove_option(opt)`. You can add a `pre_callback` override to customize the
after parse but before run behavior, while still giving the user freedom to
`callback` on the main app.
The most important parse function is `parse(std::vector<std::string>)`, which
takes a reversed list of arguments (so that `pop_back` processes the args in the
correct order). `get_help_ptr` and `get_config_ptr` give you access to the
help/config option pointers. The standard `parse` manually sets the name from
the first argument, so it should not be in this vector. You can also use
`parse(string, bool)` to split up and parse a single string; the optional
boolean should be set to true if you are including the program name in the
string, and false otherwise. The program name can contain spaces if it is an
existing file, otherwise can be enclosed in quotes(single quote, double quote or
backtick). Embedded quote characters can be escaped with `\`.
Also, in a related note, the `App` you get a pointer to is stored in the parent
`App` in a `shared_ptr`s (similar to `Option`s) and are deleted when the main
`App` goes out of scope unless the object has another owner.
### How it works
Every `add_` option you have seen so far depends on one method that takes a
lambda function. Each of these methods is just making a different lambda
function with capture to populate the option. The function has full access to
the vector of strings, so it knows how many times an option was passed or how
many arguments it received. The lambda returns `true` if it could validate the
option strings, and `false` if it failed.
Other values can be added as long as they support `operator>>` (and defaults can
be printed if they support `operator<<`). To add a new type, for example,
provide a custom `operator>>` with an `istream` (inside the CLI namespace is
fine if you don't want to interfere with an existing `operator>>`).
If you wanted to extend this to support a completely new type, use a lambda or
add an overload of the `lexical_cast` function in the namespace of the type you
need to convert to. Some examples of some new parsers for `complex<double>` that
support all of the features of a standard `add_options` call are in
[one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below:
```cpp
app.add_option("--fancy-count", [](std::vector<std::string> val){
std::cout << "This option was given " << val.size() << " times." << std::endl;
return true;
});
```
### Unicode support
CLI11 supports Unicode and wide strings as defined in the
[UTF-8 Everywhere](http://utf8everywhere.org/) manifesto. In particular:
- The library can parse a wide version of command-line arguments on Windows,
which are converted internally to UTF-8 (more on this below);
- You can store option values in `std::wstring`, in which case they will be
converted to a correct wide string encoding on your system (UTF-16 on Windows
and UTF-32 on most other systems);
- Instead of storing wide strings, it is recommended to use provided `widen` and
`narrow` functions to convert to and from wide strings when actually necessary
(such as when calling into Windows APIs).
When using the command line on Windows with unicode arguments, your `main`
function may already receive broken Unicode. Parsing `argv` at that point will
not give you a correct string. To fix this, you have three options; the first is
recommended for cross-platform support:
1\. Replace `argv` with `app.ensure_utf8(argv)` before any arguments are parsed.
`ensure_utf8` will do nothing on systems where `argv` is already in UTF-8 (Such
as Linux or macOS) and return `argv` unmodified. On Windows, it will discard
`argv` and replace it with a correctly decoded array or arguments from win32
API.
```cpp
int main(int argc, char** argv) {
CLI::App app;
argv = app.ensure_utf8(argv); // new argv memory is held by app
// ...
CLI11_PARSE(app, argc, argv);
}
```
Be sure you do not modify `argv` before this function call, as the correct
values will be reconstructed using Windows APIs and produced by this call. It
has no effect on other platforms and just passes through `argv`.
<details><summary>Other options (click to expand)</summary><p>
2\. Use the Windows-only non-standard `wmain` function, which accepts
`wchar_t *argv[]` instead of `char* argv[]`. Parsing this will allow CLI to
convert wide strings to UTF-8 without losing information.
```cpp
int wmain(int argc, wchar_t *argv[]) {
CLI::App app;
// ...
CLI11_PARSE(app, argc, argv);
}
```
3\. Retrieve arguments yourself by using Windows APIs like
[`CommandLineToArgvW`](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw)
and pass them to CLI. This is what the library is doing under the hood in
`ensure_utf8`.
</p></details>
</br>
The library provides functions to convert between UTF-8 and wide strings:
```cpp
namespace CLI {
std::string narrow(const std::wstring &str);
std::string narrow(const wchar_t *str);
std::string narrow(const wchar_t *str, std::size_t size);
std::string narrow(std::wstring_view str); // C++17
std::wstring widen(const std::string &str);
std::wstring widen(const char *str);
std::wstring widen(const char *str, std::size_t size);
std::wstring widen(std::string_view str); // C++17
}
```
#### Note on using Unicode paths
When creating a `filesystem::path` from a UTF-8 path on Windows, you need to
convert it to a wide string first. CLI11 provides a platform-independent
`to_path` function, which will convert a UTF-8 string to path, the right way:
```cpp
std::string utf8_name = "Hello HallΓ³ ΠΡΠΈΠ²Π΅Ρ δ½ ε₯½ π©βπβ€οΈ.txt";
std::filesystem::path p = CLI::to_path(utf8_name);
std::ifstream stream(CLI::to_path(utf8_name));
// etc.
```
### Utilities
There are a few other utilities that are often useful in CLI programming. These
are in separate headers, and do not appear in `CLI11.hpp`, but are completely
independent and can be used as needed. The `Timer`/`AutoTimer` class allows you
to easily time a block of code, with custom print output.
```cpp
{
CLI::AutoTimer timer {"My Long Process", CLI::Timer::Big};
some_long_running_process();
}
```
This will create a timer with a title (default: `Timer`), and will customize the
output using the predefined `Big` output (default: `Simple`). Because it is an
`AutoTimer`, it will print out the time elapsed when the timer is destroyed at
the end of the block. If you use `Timer` instead, you can use `to_string` or
`std::cout << timer << std::endl;` to print the time. The print function can be
any function that takes two strings, the title and the time, and returns a
formatted string for printing.
### Other libraries
If you use the excellent [Rang][] library to add color to your terminal in a
safe, multi-platform way, you can combine it with CLI11 nicely:
```cpp
std::atexit([](){std::cout << rang::style::reset;});
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
std::cout << (e.get_exit_code()==0 ? rang::fg::blue : rang::fg::red);
return app.exit(e);
}
```
This will print help in blue, errors in red, and will reset before returning the
terminal to the user.
If you are on a Unix-like system, and you'd like to handle control-c and color,
you can add:
```cpp
#include <csignal>
void signal_handler(int s) {
std::cout << std::endl << rang::style::reset << rang::fg::red << rang::fg::bold;
std::cout << "Control-C detected, exiting..." << rang::style::reset << std::endl;
std::exit(1); // will call the correct exit func, no unwinding of the stack though
}
```
And, in your main function:
```cpp
// Nice Control-C
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = signal_handler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, nullptr);
```
## API
The API is [documented here][api-docs]. Also see the [CLI11 tutorial
GitBook][gitbook].
## Examples
Several short examples of different features are included in the repository. A
brief description of each is included here
- [arg_capture](https://github.com/CLIUtils/CLI11/blob/main/examples/arg_capture.cpp):
Example of capturing all remaining arguments after a specific option, using
subcommand and prefix_command() with an alias.
- [callback_passthrough](https://github.com/CLIUtils/CLI11/blob/main/examples/callback_passthrough.cpp):
Example of directly passing remaining arguments through to a callback function
which generates a CLI11 application based on existing arguments.
- [custom_parse](https://github.com/CLIUtils/CLI11/blob/main/examples/custom_parse.cpp):
Based on [Issue #566](https://github.com/CLIUtils/CLI11/issues/566), example
of custom parser
- [digit_args](https://github.com/CLIUtils/CLI11/blob/main/examples/digit_args.cpp):
Based on [Issue #123](https://github.com/CLIUtils/CLI11/issues/123), uses
digit flags to pass a value
- [enum](https://github.com/CLIUtils/CLI11/blob/main/examples/enum.cpp): Using
enumerations in an option, and the use of
[CheckedTransformer](#transforming-validators)
- [enum_ostream](https://github.com/CLIUtils/CLI11/blob/main/examples/enum_ostream.cpp):
In addition to the contents of example enum.cpp, this example shows how a
custom ostream operator overrides CLI11's enum streaming.
- [formatter](https://github.com/CLIUtils/CLI11/blob/main/examples/formatter.cpp):
Illustrating usage of a custom formatter
- [groups](https://github.com/CLIUtils/CLI11/blob/main/examples/groups.cpp):
Example using groups of options for help grouping and a the timer helper class
- [inter_argument_order](https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp):
An app to practice mixing unlimited arguments, but still recover the original
order.
- [json](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp): Using
JSON as a config file parser
- [modhelp](https://github.com/CLIUtils/CLI11/blob/main/examples/modhelp.cpp):
How to modify the help flag to do something other than default
- [nested](https://github.com/CLIUtils/CLI11/blob/main/examples/nested.cpp):
Nested subcommands
- [option_groups](https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp):
Illustrating the use of option groups and a required number of options. Based
on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) to set interacting
groups of options
- [positional_arity](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_arity.cpp):
Illustrating use of `preparse_callback` to handle situations where the number
of arguments can determine which should get parsed, Based on
[Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
- [positional_validation](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_validation.cpp):
Example of how positional arguments are validated using the
`validate_positional` flag, also based on
[Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
- [prefix_command](https://github.com/CLIUtils/CLI11/blob/main/examples/prefix_command.cpp):
Illustrating use of the `prefix_command` flag.
- [ranges](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp): App
to demonstrate exclusionary option groups based on
[Issue #88](https://github.com/CLIUtils/CLI11/issues/88)
- [shapes](https://github.com/CLIUtils/CLI11/blob/main/examples/shapes.cpp):
Illustrating how to set up repeated subcommands Based on
[gitter discussion](https://gitter.im/CLI11gitter/Lobby?at=5c7af6b965ffa019ea788cd5)
- [simple](https://github.com/CLIUtils/CLI11/blob/main/examples/simple.cpp): A
simple example of how to set up a CLI11 Application with different flags and
options
- [subcom_help](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_help.cpp):
Configuring help for subcommands
- [subcom_partitioned](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_partitioned.cpp):
Example with a timer and subcommands generated separately and added to the
main app later.
- [subcommands](https://github.com/CLIUtils/CLI11/blob/main/examples/subcommands.cpp):
Short example of subcommands
- [validators](https://github.com/CLIUtils/CLI11/blob/main/examples/validators.cpp):
Example illustrating use of validators
## Contribute
To contribute, open an [issue][github issues] or [pull
request][github pull requests] on GitHub, or ask a question on [gitter][]. There
is also a short note to contributors [here](./.github/CONTRIBUTING.md). This
readme roughly follows the [Standard Readme Style][] and includes a mention of
almost every feature of the library. More complex features are documented in
more detail in the [CLI11 tutorial GitBook][gitbook].
This project was created by [Henry Schreiner](https://github.com/henryiii) and
major features were added by [Philip Top](https://github.com/phlptp). Special
thanks to all the contributors
([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://profiles.sussex.ac.uk/p281168-alex-dewar/publications"><img src="https://avatars.githubusercontent.com/u/23149834?v=4?s=100" width="100px;" alt="Alex Dewar"/><br /><sub><b>Alex Dewar</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=alexdewar" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andrew-hardin"><img src="https://avatars0.githubusercontent.com/u/16496326?v=4?s=100" width="100px;" alt="Andrew Hardin"/><br /><sub><b>Andrew Hardin</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=andrew-hardin" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/andreasxp"><img src="https://avatars.githubusercontent.com/u/28830446?v=4?s=100" width="100px;" alt="Andrey Zhukov"/><br /><sub><b>Andrey Zhukov</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=andreasxp" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SX91"><img src="https://avatars2.githubusercontent.com/u/754754?v=4?s=100" width="100px;" alt="Anton"/><br /><sub><b>Anton</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=SX91" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/trokhymchuk"><img src="https://avatars.githubusercontent.com/u/66204814?v=4?s=100" width="100px;" alt="Artem Trokhymchuk "/><br /><sub><b>Artem Trokhymchuk </b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=trokhymchuk" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BenjaminBeichler"><img src="https://avatars.githubusercontent.com/u/1441492?v=4?s=100" width="100px;" alt="Benjamin Beichler"/><br /><sub><b>Benjamin Beichler</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=BenjaminBeichler" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/calebzulawski"><img src="https://avatars.githubusercontent.com/u/563826?v=4?s=100" width="100px;" alt="Caleb Zulawski"/><br /><sub><b>Caleb Zulawski</b></sub></a><br /><a href="#platform-calebzulawski" title="Packaging/porting to new platform">π¦</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/krico"><img src="https://avatars.githubusercontent.com/u/6952185?v=4?s=100" width="100px;" alt="Christian Asmussen"/><br /><sub><b>Christian Asmussen</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=krico" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/cbachhuber/"><img src="https://avatars0.githubusercontent.com/u/27212661?v=4?s=100" width="100px;" alt="Christoph Bachhuber"/><br /><sub><b>Christoph Bachhuber</b></sub></a><br /><a href="#example-cbachhuber" title="Examples">π‘</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=cbachhuber" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dfleury2"><img src="https://avatars1.githubusercontent.com/u/4805384?v=4?s=100" width="100px;" alt="D. Fleury"/><br /><sub><b>D. Fleury</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dfleury2" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dbarowy"><img src="https://avatars3.githubusercontent.com/u/573142?v=4?s=100" width="100px;" alt="Dan Barowy"/><br /><sub><b>Dan Barowy</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dbarowy" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mensinda"><img src="https://avatars3.githubusercontent.com/u/3407462?v=4?s=100" width="100px;" alt="Daniel Mensinger"/><br /><sub><b>Daniel Mensinger</b></sub></a><br /><a href="#platform-mensinda" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DarkWingMcQuack"><img src="https://avatars.githubusercontent.com/u/38857302?v=4?s=100" width="100px;" alt="DarkWingMcQuack"/><br /><sub><b>DarkWingMcQuack</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=DarkWingMcQuack" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ZeeD26"><img src="https://avatars2.githubusercontent.com/u/2487468?v=4?s=100" width="100px;" alt="Dominik Steinberger"/><br /><sub><b>Dominik Steinberger</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ZeeD26" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dvj"><img src="https://avatars2.githubusercontent.com/u/77217?v=4?s=100" width="100px;" alt="Doug Johnston"/><br /><sub><b>Doug Johnston</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Advj" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=dvj" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://recursiveascent.blogspot.com/"><img src="https://avatars.githubusercontent.com/u/1779595?v=4?s=100" width="100px;" alt="Dylan Baker"/><br /><sub><b>Dylan Baker</b></sub></a><br /><a href="#platform-dcbaker" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/eli-schwartz"><img src="https://avatars.githubusercontent.com/u/6551424?v=4?s=100" width="100px;" alt="Eli Schwartz"/><br /><sub><b>Eli Schwartz</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=eli-schwartz" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sifferman"><img src="https://avatars.githubusercontent.com/u/43790149?v=4?s=100" width="100px;" alt="Ethan Sifferman"/><br /><sub><b>Ethan Sifferman</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=sifferman" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/helmesjo"><img src="https://avatars0.githubusercontent.com/u/2501070?v=4?s=100" width="100px;" alt="Fred HelmesjΓΆ"/><br /><sub><b>Fred HelmesjΓΆ</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ahelmesjo" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=helmesjo" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://iscinumpy.gitlab.io"><img src="https://avatars1.githubusercontent.com/u/4616906?v=4?s=100" width="100px;" alt="Henry Schreiner"/><br /><sub><b>Henry Schreiner</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ahenryiii" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=henryiii" title="Documentation">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=henryiii" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://izzys.casa"><img src="https://avatars0.githubusercontent.com/u/63051?v=4?s=100" width="100px;" alt="Isabella Muerte"/><br /><sub><b>Isabella Muerte</b></sub></a><br /><a href="#platform-slurps-mad-rips" title="Packaging/porting to new platform">π¦</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://izzys.casa/"><img src="https://avatars.githubusercontent.com/u/63051?v=4?s=100" width="100px;" alt="Izzy Muerte"/><br /><sub><b>Izzy Muerte</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=bruxisma" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jakoblover"><img src="https://avatars0.githubusercontent.com/u/14160441?v=4?s=100" width="100px;" alt="Jakob Lover"/><br /><sub><b>Jakob Lover</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=jakoblover" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jgerityneurala"><img src="https://avatars2.githubusercontent.com/u/57360646?v=4?s=100" width="100px;" alt="James Gerity"/><br /><sub><b>James Gerity</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=jgerityneurala" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jbriales"><img src="https://avatars1.githubusercontent.com/u/6850478?v=4?s=100" width="100px;" alt="Jesus Briales"/><br /><sub><b>Jesus Briales</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=jbriales" title="Code">π»</a> <a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Ajbriales" title="Bug reports">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SkyToGround"><img src="https://avatars1.githubusercontent.com/u/58835?v=4?s=100" width="100px;" alt="Jonas Nilsson"/><br /><sub><b>Jonas Nilsson</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3ASkyToGround" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=SkyToGround" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/j-rivero"><img src="https://avatars.githubusercontent.com/u/2098802?v=4?s=100" width="100px;" alt="Jose Luis Rivero"/><br /><sub><b>Jose Luis Rivero</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=j-rivero" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jsoref"><img src="https://avatars0.githubusercontent.com/u/2119212?v=4?s=100" width="100px;" alt="Josh Soref"/><br /><sub><b>Josh Soref</b></sub></a><br /><a href="#tool-jsoref" title="Tools">π§</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KOLANICH"><img src="https://avatars1.githubusercontent.com/u/240344?v=4?s=100" width="100px;" alt="KOLANICH"/><br /><sub><b>KOLANICH</b></sub></a><br /><a href="#platform-KOLANICH" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/skannan89"><img src="https://avatars0.githubusercontent.com/u/11918764?v=4?s=100" width="100px;" alt="Kannan"/><br /><sub><b>Kannan</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Askannan89" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=skannan89" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://himvis.com"><img src="https://avatars3.githubusercontent.com/u/465279?v=4?s=100" width="100px;" alt="Khem Raj"/><br /><sub><b>Khem Raj</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=kraj" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/looopTools"><img src="https://avatars.githubusercontent.com/u/1943536?v=4?s=100" width="100px;" alt="Lars Nielsen"/><br /><sub><b>Lars Nielsen</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=looopTools" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://lucas-czech.de"><img src="https://avatars0.githubusercontent.com/u/4741887?v=4?s=100" width="100px;" alt="Lucas Czech"/><br /><sub><b>Lucas Czech</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Alczech" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=lczech" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.mogigoma.com/"><img src="https://avatars2.githubusercontent.com/u/130862?v=4?s=100" width="100px;" alt="Mak Kolybabi"/><br /><sub><b>Mak Kolybabi</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=mogigoma" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cetius"><img src="https://avatars.githubusercontent.com/u/6552472?v=4?s=100" width="100px;" alt="Marcin Ropa"/><br /><sub><b>Marcin Ropa</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=cetius" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://lambdafu.net/"><img src="https://avatars1.githubusercontent.com/u/1138455?v=4?s=100" width="100px;" alt="Marcus Brinkmann"/><br /><sub><b>Marcus Brinkmann</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Alambdafu" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=lambdafu" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://msoeken.github.io"><img src="https://avatars0.githubusercontent.com/u/1998245?v=4?s=100" width="100px;" alt="Mathias Soeken"/><br /><sub><b>Mathias Soeken</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=msoeken" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.mmmccormick.com/"><img src="https://avatars.githubusercontent.com/u/25432?v=4?s=100" width="100px;" alt="Matt McCormick"/><br /><sub><b>Matt McCormick</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=thewtex" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AnticliMaxtic"><img src="https://avatars.githubusercontent.com/u/43995389?v=4?s=100" width="100px;" alt="Max"/><br /><sub><b>Max</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=AnticliMaxtic" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://mbh.sh"><img src="https://avatars3.githubusercontent.com/u/20403931?v=4?s=100" width="100px;" alt="Michael Hall"/><br /><sub><b>Michael Hall</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=mbhall88" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nathanhourt"><img src="https://avatars2.githubusercontent.com/u/271977?v=4?s=100" width="100px;" alt="Nathan Hourt"/><br /><sub><b>Nathan Hourt</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Anathanhourt" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=nathanhourt" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nathanielhourt"><img src="https://avatars.githubusercontent.com/u/271977?v=4?s=100" width="100px;" alt="Nathaniel Hourt"/><br /><sub><b>Nathaniel Hourt</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=nathanielhourt" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/paddy-hack"><img src="https://avatars.githubusercontent.com/u/6804372?v=4?s=100" width="100px;" alt="Olaf Meeuwissen"/><br /><sub><b>Olaf Meeuwissen</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=paddy-hack" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ondrejcertik.com/"><img src="https://avatars3.githubusercontent.com/u/20568?v=4?s=100" width="100px;" alt="OndΕej ΔertΓk"/><br /><sub><b>OndΕej ΔertΓk</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Acertik" title="Bug reports">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pleroux0"><img src="https://avatars2.githubusercontent.com/u/39619854?v=4?s=100" width="100px;" alt="Paul le Roux"/><br /><sub><b>Paul le Roux</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=pleroux0" title="Code">π»</a> <a href="#platform-pleroux0" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chfast"><img src="https://avatars1.githubusercontent.com/u/573380?v=4?s=100" width="100px;" alt="PaweΕ Bylica"/><br /><sub><b>PaweΕ Bylica</b></sub></a><br /><a href="#platform-chfast" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeteAudinate"><img src="https://avatars.githubusercontent.com/u/99274874?v=4?s=100" width="100px;" alt="PeteAudinate"/><br /><sub><b>PeteAudinate</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=PeteAudinate" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/peterazmanov"><img src="https://avatars0.githubusercontent.com/u/15322318?v=4?s=100" width="100px;" alt="Peter Azmanov"/><br /><sub><b>Peter Azmanov</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=peterazmanov" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/peterh"><img src="https://avatars.githubusercontent.com/u/79339?v=4?s=100" width="100px;" alt="Peter Harris"/><br /><sub><b>Peter Harris</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=peterh" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://ptheywood.uk/"><img src="https://avatars.githubusercontent.com/u/628937?v=4?s=100" width="100px;" alt="Peter Heywood"/><br /><sub><b>Peter Heywood</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ptheywood" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/phlptp"><img src="https://avatars0.githubusercontent.com/u/20667153?v=4?s=100" width="100px;" alt="Philip Top"/><br /><sub><b>Philip Top</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Aphlptp" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=phlptp" title="Documentation">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=phlptp" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rafiw"><img src="https://avatars3.githubusercontent.com/u/3034707?v=4?s=100" width="100px;" alt="Rafi Wiener"/><br /><sub><b>Rafi Wiener</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Arafiw" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=rafiw" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RangeMachine"><img src="https://avatars.githubusercontent.com/u/11577601?v=4?s=100" width="100px;" alt="RangeMachine"/><br /><sub><b>RangeMachine</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=RangeMachine" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Krzmbrzl"><img src="https://avatars.githubusercontent.com/u/12751591?v=4?s=100" width="100px;" alt="Robert Adam"/><br /><sub><b>Robert Adam</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=Krzmbrzl" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.ratml.org/"><img src="https://avatars0.githubusercontent.com/u/1845039?v=4?s=100" width="100px;" alt="Ryan Curtin"/><br /><sub><b>Ryan Curtin</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=rcurtin" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SherlockInSpace"><img src="https://avatars.githubusercontent.com/u/5507786?v=4?s=100" width="100px;" alt="Ryan Sherlock"/><br /><sub><b>Ryan Sherlock</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=SherlockInSpace" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://sam.hocevar.net/"><img src="https://avatars2.githubusercontent.com/u/245089?v=4?s=100" width="100px;" alt="Sam Hocevar"/><br /><sub><b>Sam Hocevar</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=samhocevar" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://seanfisk.com/"><img src="https://avatars0.githubusercontent.com/u/410322?v=4?s=100" width="100px;" alt="Sean Fisk"/><br /><sub><b>Sean Fisk</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/issues?q=author%3Aseanfisk" title="Bug reports">π</a> <a href="https://github.com/CLIUtils/CLI11/commits?author=seanfisk" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delpinux"><img src="https://avatars0.githubusercontent.com/u/35096584?v=4?s=100" width="100px;" alt="StΓ©phane Del Pino"/><br /><sub><b>StΓ©phane Del Pino</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=delpinux" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/metopa"><img src="https://avatars2.githubusercontent.com/u/3974178?v=4?s=100" width="100px;" alt="Viacheslav Kroilov"/><br /><sub><b>Viacheslav Kroilov</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=metopa" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VolkerChristian"><img src="https://avatars.githubusercontent.com/u/18554540?v=4?s=100" width="100px;" alt="Volker Christian"/><br /><sub><b>Volker Christian</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=VolkerChristian" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/almikhayl"><img src="https://avatars2.githubusercontent.com/u/6747040?v=4?s=100" width="100px;" alt="almikhayl"/><br /><sub><b>almikhayl</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=almikhayl" title="Code">π»</a> <a href="#platform-almikhayl" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ayum"><img src="https://avatars.githubusercontent.com/u/6747040?v=4?s=100" width="100px;" alt="ayum"/><br /><sub><b>ayum</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ayum" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/captainurist"><img src="https://avatars.githubusercontent.com/u/73941350?v=4?s=100" width="100px;" alt="captainurist"/><br /><sub><b>captainurist</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=captainurist" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://cs.odu.edu/~ctsolakis"><img src="https://avatars0.githubusercontent.com/u/6725596?v=4?s=100" width="100px;" alt="christos"/><br /><sub><b>christos</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ChristosT" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/deining"><img src="https://avatars3.githubusercontent.com/u/18169566?v=4?s=100" width="100px;" alt="deining"/><br /><sub><b>deining</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=deining" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dherrera-fb"><img src="https://avatars.githubusercontent.com/u/89840711?v=4?s=100" width="100px;" alt="dherrera-fb"/><br /><sub><b>dherrera-fb</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dherrera-fb" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/djerius"><img src="https://avatars.githubusercontent.com/u/196875?v=4?s=100" width="100px;" alt="djerius"/><br /><sub><b>djerius</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=djerius" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dryleev"><img src="https://avatars.githubusercontent.com/u/83670813?v=4?s=100" width="100px;" alt="dryleev"/><br /><sub><b>dryleev</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dryleev" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/elszon"><img src="https://avatars0.githubusercontent.com/u/2971495?v=4?s=100" width="100px;" alt="elszon"/><br /><sub><b>elszon</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=elszon" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ferdymercury"><img src="https://avatars3.githubusercontent.com/u/10653970?v=4?s=100" width="100px;" alt="ferdymercury"/><br /><sub><b>ferdymercury</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ferdymercury" title="Documentation">π</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fpeng1985"><img src="https://avatars1.githubusercontent.com/u/87981?v=4?s=100" width="100px;" alt="fpeng1985"/><br /><sub><b>fpeng1985</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=fpeng1985" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/geir-t"><img src="https://avatars3.githubusercontent.com/u/35292136?v=4?s=100" width="100px;" alt="geir-t"/><br /><sub><b>geir-t</b></sub></a><br /><a href="#platform-geir-t" title="Packaging/porting to new platform">π¦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gostefan"><img src="https://avatars.githubusercontent.com/u/2479455?v=4?s=100" width="100px;" alt="gostefan"/><br /><sub><b>gostefan</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=gostefan" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ncihnegn"><img src="https://avatars3.githubusercontent.com/u/12021721?v=4?s=100" width="100px;" alt="ncihnegn"/><br /><sub><b>ncihnegn</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ncihnegn" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nurelin"><img src="https://avatars3.githubusercontent.com/u/5276274?v=4?s=100" width="100px;" alt="nurelin"/><br /><sub><b>nurelin</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=nurelin" title="Code">π»</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://polistern.i2p/"><img src="https://avatars.githubusercontent.com/u/55511995?v=4?s=100" width="100px;" alt="polistern"/><br /><sub><b>polistern</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=polistern" title="Code">π»</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ryan4729"><img src="https://avatars3.githubusercontent.com/u/40183301?v=4?s=100" width="100px;" alt="ryan4729"/><br /><sub><b>ryan4729</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ryan4729" title="Tests">β οΈ</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shameekganguly"><img src="https://avatars.githubusercontent.com/u/2412842?v=4?s=100" width="100px;" alt="shameekganguly"/><br /><sub><b>shameekganguly</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=shameekganguly" title="Code">π»</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the
[all-contributors](https://github.com/all-contributors/all-contributors)
specification. Contributions of any kind welcome!
## License
As of version 1.0, this library is available under a 3-Clause BSD license. See
the [LICENSE](./LICENSE) file for details.
CLI11 was developed at the [University of Cincinnati][] to support of the
[GooFit][] library under [NSF Award 1414736][]. Version 0.9 was featured in a
[DIANA/HEP][] meeting at CERN ([see the slides][diana slides]). Please give it a
try! Feedback is always welcome.
[doi-badge]: https://zenodo.org/badge/80064252.svg
[doi-link]: https://zenodo.org/badge/latestdoi/80064252
[azure-badge]:
https://dev.azure.com/CLIUtils/CLI11/_apis/build/status/CLIUtils.CLI11?branchName=main
[azure]: https://dev.azure.com/CLIUtils/CLI11
[actions-link]: https://github.com/CLIUtils/CLI11/actions
[actions-badge]:
https://github.com/CLIUtils/CLI11/actions/workflows/tests.yml/badge.svg
[repology-badge]: https://repology.org/badge/latest-versions/cli11.svg
[repology]: https://repology.org/project/cli11/versions
[codecov-badge]:
https://codecov.io/gh/CLIUtils/CLI11/branch/main/graph/badge.svg?token=2O4wfs8NJO
[codecov]: https://codecov.io/gh/CLIUtils/CLI11
[gitter-badge]: https://badges.gitter.im/CLI11gitter/Lobby.svg
[gitter]: https://gitter.im/CLI11gitter/Lobby
[license-badge]: https://img.shields.io/badge/License-BSD-blue.svg
[conan-badge]: https://img.shields.io/badge/conan-io-blue
[conan-link]: https://conan.io/center/cli11
[conda-badge]: https://img.shields.io/conda/vn/conda-forge/cli11.svg
[conda-link]: https://github.com/conda-forge/cli11-feedstock
[github releases]: https://github.com/CLIUtils/CLI11/releases
[github issues]: https://github.com/CLIUtils/CLI11/issues
[github pull requests]: https://github.com/CLIUtils/CLI11/pulls
[goofit]: https://GooFit.github.io
[plumbum]: https://plumbum.readthedocs.io/en/latest/
[click]: http://click.pocoo.org
[api-docs]: https://CLIUtils.github.io/CLI11/index.html
[rang]: https://github.com/agauniyal/rang
[boost program options]:
http://www.boost.org/doc/libs/1_63_0/doc/html/program_options.html
[the lean mean c++ option parser]: http://optionparser.sourceforge.net
[tclap]: http://tclap.sourceforge.net
[cxxopts]: https://github.com/jarro2783/cxxopts
[docopt]: https://github.com/docopt/docopt.cpp
[gflags]: https://gflags.github.io/gflags
[getopt]: https://www.gnu.org/software/libc/manual/html_node/Getopt.html
[diana/hep]: http://diana-hep.org
[nsf award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
[university of cincinnati]: http://www.uc.edu
[gitbook]: https://cliutils.github.io/CLI11/book/
[cli11 advanced topics/custom converters]:
https://cliutils.gitlab.io/CLI11Tutorial/chapters/advanced-topics.html#custom-converters
[programoptions.hxx]: https://github.com/Fytch/ProgramOptions.hxx
[argument aggregator]: https://github.com/vietjtnguyen/argagg
[args]: https://github.com/Taywee/args
[argh!]: https://github.com/adishavit/argh
[fmt]: https://github.com/fmtlib/fmt
[catch]: https://github.com/philsquared/Catch
[clara]: https://github.com/philsquared/Clara
[version 1.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-10/
[version 1.3 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-13/
[version 1.6 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-16/
[version 2.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-20/
[wandbox-badge]: https://img.shields.io/badge/try_2.1-online-blue.svg
[wandbox-link]: https://wandbox.org/permlink/9eQyaD1DchlzukRv
[releases-badge]: https://img.shields.io/github/release/CLIUtils/CLI11.svg
[cli11-po-compare]:
https://iscinumpy.gitlab.io/post/comparing-cli11-and-boostpo/
[diana slides]:
https://indico.cern.ch/event/619465/contributions/2507949/attachments/1448567/2232649/20170424-diana-2.pdf
[awesome c++]: https://github.com/fffaraz/awesome-cpp/blob/master/README.md#cli
[cli]: https://codesynthesis.com/projects/cli/
[single file libs]:
https://github.com/nothings/single_file_libs/blob/master/README.md
[codacy-badge]:
https://app.codacy.com/project/badge/Grade/2796b969c1b54321a02ad08affec0800
[codacy-link]:
https://www.codacy.com/gh/CLIUtils/CLI11/dashboard?utm_source=github.com&utm_medium=referral&utm_content=CLIUtils/CLI11&utm_campaign=Badge_Grade
[hunter]: https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html
[standard readme style]: https://github.com/RichardLitt/standard-readme
[argparse]: https://github.com/p-ranav/argparse
[toml]: https://toml.io
[lyra]: https://github.com/bfgroup/Lyra
[installation]: https://cliutils.github.io/CLI11/book/chapters/installation.html
", Assign "at most 3 tags" to the expected json: {"id":"8425","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"