This commit is contained in:
2026-09-13 13:30:21 +08:00
commit a6bbd520cf
2744 changed files with 1598795 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# Node rules:
## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
## Dependency directory
## Commenting this out is preferred by some people, see
## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
node_modules
# Book build output
_book
# eBook build output
*.epub
*.mobi
*.pdf
a.out
*build*
+13
View File
@@ -0,0 +1,13 @@
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
# under NSF AWARD 1414736 and by the respective contributors.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
set(book_sources README.md)
file(
GLOB book_chapters
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
chapters/*.md)
add_custom_target(cli_book SOURCES ${book_sources} ${book_chapters})
+35
View File
@@ -0,0 +1,35 @@
# Guide
This guide walks through CLI11 chapter by chapter, from a first simple program
to subcommands, configuration files, and the library internals. Read it in
order, or jump to the chapter you need:
- [Installation](chapters/installation.md)
- [The Basics](chapters/basics.md)
- [Adding Flags](chapters/flags.md)
- [Options](chapters/options.md)
- [Validators](chapters/validators.md)
- [Subcommands and the App](chapters/subcommands.md)
- [Option groups](chapters/option-groups.md)
- [Accepting configure files](chapters/config.md)
- [Formatting help output](chapters/formatting.md)
- [Unicode support](chapters/unicode.md)
- [Using CLI11 in a Toolkit](chapters/toolkits.md)
- [Advanced topics](chapters/advanced-topics.md)
- [CLI11 Internals](chapters/internals.md)
## Examples
- [Making a git clone](chapters/an-advanced-example.md) — a walkthrough of a
larger program
- [Using CLI11 as a C++20 module](chapters/modules-example.md) — complete
`import cli11;` programs, with an `import std;` variant
- [Example programs](../examples) — small, complete programs you can copy
A rendered version is part of the
[CLI11 documentation](https://cliutils.github.io/CLI11/).
> Feel free to contribute to [this documentation here][cli11guide] if something
> can be improved!
[cli11guide]: https://github.com/CLIUtils/CLI11/tree/main/book
+148
View File
@@ -0,0 +1,148 @@
# Advanced topics {#book-advanced-topics}
## Environment variables
Environment variables can be used to fill in the value of an option:
```cpp
std::string opt;
app.add_option("--my_option", opt)->envname("MY_OPTION");
```
If not given on the command line, the environment variable will be checked and
read from if it exists. All the standard tools, like default and required, work
as expected. If passed on the command line, this will ignore the environment
variable.
## Needs/excludes
You can set a network of requirements. For example, if flag a needs flag b but
cannot be given with flag c, that would be:
```cpp
auto a = app.add_flag("-a");
auto b = app.add_flag("-b");
auto c = app.add_flag("-c");
a->needs(b);
a->excludes(c);
```
CLI11 will make sure your network of requirements makes sense, and will throw an
error immediately if it does not.
## Custom option callbacks
You can make a completely generic option with a custom callback. For example, if
you wanted to add a complex number (already exists, so please don't actually do
this):
```cpp
CLI::Option *
add_option(CLI::App &app, std::string name, cx &variable, std::string description = "", bool defaulted = false) {
CLI::callback_t fun = [&variable](CLI::results_t res) {
double x, y;
bool worked = CLI::detail::lexical_cast(res[0], x) && CLI::detail::lexical_cast(res[1], y);
if(worked)
variable = cx(x, y);
return worked;
};
CLI::Option *opt = app.add_option(name, fun, description, defaulted);
opt->type_name("COMPLEX");
opt->type_size(2);
if(defaulted) {
std::stringstream out;
out << variable;
opt->default_str(out.str());
}
return opt;
}
```
Then you could use it like this:
```cpp
std::complex<double> comp{0, 0};
add_option(app, "-c,--complex", comp);
```
## Timers
`CLI/Timer.hpp` is independent of the rest of the library and is not part of
`CLI11.hpp` single file header. It times a block of code:
```cpp
#include <CLI/Timer.hpp>
{
CLI::AutoTimer timer{"My Long Process", CLI::Timer::Big};
some_long_running_process();
}
```
An `AutoTimer` prints the elapsed time when it is destroyed at the end of the
block. A plain `Timer` does not; use `to_string()` or stream it when you want
the value. The first argument is the title, which defaults to `Timer`. The
second is the print function, either the provided `CLI::Timer::Simple` (the
default) or `CLI::Timer::Big`, or any function that takes the title and the time
as strings and returns the line to print. `time_it(func)` runs a function
repeatedly and reports the average.
## Custom converters
You can add your own converters to allow CLI11 to accept more option types in
the standard calls. These can only be used for "single" size options (so
complex, vector, etc. are a separate topic). If you set up a custom
`istringstream& operator>>` overload before include CLI11, you can support
different conversions. If you place this in the CLI namespace, you can even keep
this from affecting the rest of your code. Note that `std::optional` is
supported natively and does not need a custom converter. The next section shows
a complete example.
## Custom converters and type names: std::chrono example
An example of adding a custom converter and typename for `std::chrono` follows:
```cpp
namespace CLI
{
template <typename T, typename R>
std::istringstream &operator>>(std::istringstream &in, std::chrono::duration<T,R> &val)
{
T v;
in >> v;
val = std::chrono::duration<T,R>(v);
return in;
}
template <typename T, typename R>
std::stringstream &operator<<(std::stringstream &in, std::chrono::duration<T,R> &val)
{
in << val.count();
return in;
}
}
#include <CLI/CLI.hpp>
namespace CLI
{
namespace detail
{
template <>
constexpr const char *type_name<std::chrono::hours>()
{
return "TIME [H]";
}
template <>
constexpr const char *type_name<std::chrono::minutes>()
{
return "TIME [MIN]";
}
}
}
```
Thanks to Olivier Hartmann for the example.
@@ -0,0 +1,39 @@
# Making a git clone {#book-an-advanced-example}
Let's try our hand at a little `git` clone, called `geet`. It will just print
it's intent, rather than running actual code, since it's just a demonstration.
Let's start by adding an app and requiring 1 subcommand to run:
\snippet geet.cpp Intro
Now, let's define the first subcommand, `add`, along with a few options:
\snippet geet.cpp Add
Now, let's add `commit`:
\snippet geet.cpp Commit
All that's need now is the parse call. We'll print a little message after the
code runs, and then return:
\snippet geet.cpp Parse
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/geet.cpp)
If you compile and run:
```text
c++ -std=c++11 geet.cpp -o geet
```
You'll see it behaves pretty much like `git`.
## Multi-file App parse code
This example could be made much nicer if it was split into files, one per
subcommand. If you simply use shared pointers instead of raw values in the
lambda capture, you can tie the lifetime to the lambda function lifetime. CLI11
has a
[multifile example](https://github.com/CLIUtils/CLI11/tree/main/examples/subcom_in_files)
in its example folder.
+41
View File
@@ -0,0 +1,41 @@
# The Basics {#book-basics}
The simplest CLI11 program looks like this:
\include simplest.cpp
The first line includes the library; this uses the normal headers of the full
edition (see [Selecting an edition](@ref book-installation)). With the single
file edition you would include `CLI11.hpp` instead.
After entering the main function, you'll see that a `CLI::App` object is
created. This is the basis for all interactions with the library. You could
optionally provide a description for your app here.
A normal CLI11 application would define some flags and options next. This is a
simplest possible example, so we'll go on.
The macro `CLI11_PARSE` just runs five simple lines. This internally runs
`app.parse(argc, argv)`, which takes the command line info from C++ and parses
it. If there is an error, it throws a `ParseError`; if you catch it, you can use
`app.exit` with the error as an argument to print a nice message and produce the
correct return code for your application.
If you just use `app.parse` directly, your application will still work, but the
stack will not be correctly unwound since you have an uncaught exception, and
the command line output will be cluttered, especially for help.
For this (and most of the examples in this book) we will assume that the CLI11
`include` directory is on the compiler include path and that we are creating an
output executable `a.out` on a macOS or Linux system. (With the single file
edition, you could instead place `CLI11.hpp` next to your source code and skip
the include path.) The commands to compile and test this example would be:
```text
$ g++ -std=c++11 simplest.cpp -I/path/to/CLI11/include
$ ./a.out -h
Usage: ./a.out [OPTIONS]
Options:
-h,--help Print this help message and exit
```
+548
View File
@@ -0,0 +1,548 @@
# Accepting configure files {#book-config}
## Reading a configure file
You can tell your app to allow configure files with `set_config("--config")`.
There are four arguments: the first is the option name. If empty, it will clear
the config flag. The second item is the default file name. If that is specified,
the config will try to read that file. The third item is the help string, with a
reasonable default, and the final argument is a boolean (default: false) that
indicates that the configuration file is required and an error will be thrown if
the file is not found and this is set to true. The option pointer returned by
`set_config` is the same type as returned by `add_option` and all modifiers
including validators, and checks are valid.
### Adding a default path
if it is desired that config files be searched for a in a default path the
`CLI::FileOnDefaultPath` transform can be used.
```cpp
app.set_config("--config")->transform(CLI::FileOnDefaultPath("/default_path/"));
```
This will allow specified files to either exist as given or on a specified
default path.
```cpp
app.set_config("--config")
->transform(CLI::FileOnDefaultPath("/default_path/"))
->transform(CLI::FileOnDefaultPath("/default_path2/",false));
```
Multiple default paths can be specified through this mechanism. The last
transform given is executed first so the error return must be disabled so it can
be chained to the first. The same effect can be achieved though the or(`|`)
operation with validators
```cpp
app.set_config("--config")
->transform(CLI::FileOnDefaultPath("/default_path2/") | CLI::FileOnDefaultPath("/default_path/"));
```
### Extra fields
Sometimes configuration files are used for multiple purposes so CLI11 allows
options on how to deal with extra fields
```cpp
app.allow_config_extras(true);
```
will allow capture the extras in the extras field of the app. (NOTE: This also
sets the `allow_extras` in the app to true)
```cpp
app.allow_config_extras(false);
```
will generate an error if there are any extra fields for slightly finer control
there is a scoped enumeration of the modes or
```cpp
app.allow_config_extras(CLI::config_extras_mode::ignore);
```
will completely ignore extra parameters in the config file. This mode is the
default.
```cpp
app.allow_config_extras(CLI::config_extras_mode::capture);
```
will store the unrecognized options in the app extras fields. This option is the
closest equivalent to `app.allow_config_extras(true);` with the exception that
it does not also set the `allow_extras` flag so using this option without also
setting `allow_extras(true)` will generate an error which may or may not be the
desired behavior.
```cpp
app.allow_config_extras(CLI::config_extras_mode::error);
```
is equivalent to `app.allow_config_extras(false);`
```cpp
app.allow_config_extras(CLI::config_extras_mode::ignore_all);
```
will completely ignore any mismatches, extras, or other issues with the config
file
Config file extras are stored in the remaining output as two components. The
first is the name of the field including subcommands using dot notation the
second (or more) are the argument fields.
### Getting the used configuration file name
If it is needed to get the configuration file name used this can be obtained via
`app.get_config_ptr()->as<std::string>()` or
`app["--config"]->as<std::string>()` assuming `--config` was the configuration
option name.
### Order of precedence
By default if multiple configuration files are given they are read in reverse
order. With the last one given taking precedence over the earlier ones. This
behavior can be changed through the `multi_option_policy`. For example:
```cpp
app.set_config("--config")
->multi_option_policy(CLI::MultiOptionPolicy::TakeAll);
```
will read the files in the order given, which may be useful in some
circumstances. Using `CLI::MultiOptionPolicy::TakeLast` would work similarly
getting the last `N` files given. The default policy for config options is
`CLI::MultiOptionPolicy::Reverse` which takes the last expected `N` and reverses
them so the last option given is given precedence.
## Configure file format
Here is an example configuration file, in
[TOML](https://github.com/toml-lang/toml) format:
```toml
# Comments are supported, using a #
# The default section is [default], case-insensitive
value = 1
str = "A string"
vector = [1,2,3]
# Section map to subcommands
[subcommand]
in_subcommand = "Wow"
[subcommand.sub]
subcommand = true # could also be give as 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`,
`y`, `t`, `+`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `n`, `f`, `-`,
`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". If a subcommand is set to `configurable` then passing
the subcommand using `[sub]` in a configuration file will trigger the
subcommand.)
CLI11 also supports configuration file 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
; Section map to subcommands
[subcommand]
in_subcommand = Wow
sub.subcommand = true
```
The main differences are in vector notation and comment character. Note: CLI11
is not a full TOML parser as it just reads values as strings. It is possible
(but not recommended) to mix notation.
### Multi-line strings
The default config file parser supports multi-line strings like the toml
standard [TOML](https://toml.io/en/). It also supports multiline comments like
python doc strings.
```toml
"""
this is a multiline
comment
"""
""" this is also
a multiline comment"""
''' and so is
this
'''
value = 1
str = """
this is a multiline string value
the first \n is removed and so is the last
"""
str2 = ''' this is also a mu-
ltiline value '''
str3 = """\
a line continuation \
will skip \
all white space between the '\' \
and the next non-whitespace character \
making this into a single line
"""
```
The key is that the closing of the multiline string must be at the end of a line
and match the starting 3 quote sequence. Multiline sequences using `"""` allow
escape sequences. Following [TOML](https://toml.io/en/v1.0.0#string) with the
addition of allowing '\0' for a null character, and binary Strings described in
the next section. This same formatting also applies to single line strings.
Multiline strings are not allowed as part of an array.
### Binary Strings
Config files have a binary conversion capability, this is mainly to support
writing config files but can be used by user generated files as well. Strings
with the form `B"(XXXXX)"` will convert any characters inside the parenthesis
with the form `\xHH` to the equivalent binary value. The HH are hexadecimal
characters. Characters not in this form will be translated as given. If argument
values with unprintable characters are used to generate a config file this
binary form will be used in the output string.
### multiline vector
Vector arguments can also be multiline
```toml
#multiline vector format
vector = [
2,
3,
4,
5,
6
]
```
### vector of vector inputs
It is possible to specify vector of vector inputs in config file. This can be
done in a few different ways
```toml
# Examples of vector of vector inputs in config
# this example is how config_to_str writes it out
vector1 = [1,2,3,"",4,5,6]
# alternative with vector separator sequence
vector2 = [1,2,3,"%%",4,5,6]
# multiline format
vector3 = [1,2,3]
vector3 = [4,5,6]
```
The `%%` is ignored in multiline format if the inject_separator modifier on the
option is set to false, thus for vector 3 if the option is storing to a single
vector all the elements will be in that vector.
For config file multiple sequential duplicate variable names are treated as if
they are a vector input, with possible separator insertion in the case of
multiple input vectors.
The config parser has a modifier
```C++
app.get_config_formatter_base()->allowDuplicateFields();
```
This modification will insert the separator between each line even if not
sequential. This allows an input option to be configured with multiple lines.
```toml
# Examples of vector of vector inputs in config
# this example is how config_to_str writes it out
vector1 = [a,v,"[]"]
```
The field insertion has a special processing for duplicate characters starting
with "[[" in which case the `"[]"` gets translated to `[[]]` before getting
passed into the option which converts it back into the correct string. This can
also be used on the command line to handle unusual parsing situation with
brackets.
### Argument With Brackets
There is an edge case with actual strings that are surrounded by brackets. For
example if the string "[]" needed to be passed. this would normally trigger the
bracket processing and result in an empty vector. In this case it can be
enclosed in quotes and should be handled correctly.
## Multiple configuration files
If it is desired that multiple configuration be allowed. Use
```cpp
app.set_config("--config")->expected(1, X);
```
Where X is some positive integer and will allow up to `X` configuration files to
be specified by separate `--config` arguments.
## Writing out a configure file
To print a configuration file from the passed arguments, use one of the
following overloads:
- `.config_to_str()`: Print active values only.
- `.config_to_str(bool default_also, bool write_description = false)`: Print
active values, or include defaulted arguments if `default_also` is `true`.
This overload will likely be deprecated in a future release in favor of the
`CLI::ConfigOutputMode` overload.
- `.config_to_str(CLI::ConfigOutputMode, bool write_description = false)`: 🆕
Specify how configuration output should be generated.
- `CLI::ConfigOutputMode::Active`: print active values only. Same as
`.config_to_str()`.
- `CLI::ConfigOutputMode::AllDefaults`: include defaulted arguments for the
app and all subcommands. Same as `.config_to_str(true, ...)`.
- `CLI::ConfigOutputMode::ActiveSubcommandDefaults`: include defaulted
arguments for the app and active subcommands, while omitting defaults from
inactive subcommands.
The `write_description` argument will include option descriptions and the App
description.
Subcommands are written in one of two forms. A subcommand set to
`configurable()` gets its own section, such as `[sub]`, if the subcommand was
passed on the command line or the mode is `CLI::ConfigOutputMode::AllDefaults`.
In all other conditions, the options of the subcommand are written with dotted
names, such as `sub.value=1`. Nested subcommands use the parent names as a
prefix, such as `[sub.subsub]` or `sub.subsub.value=1`.
```cpp
CLI::App app;
int a{1}, b{1};
app.add_subcommand("plain")->add_option("--value", a)->capture_default_str();
app.add_subcommand("configurable")->configurable()
->add_option("--value", b)->capture_default_str();
std::cout<<app.config_to_str(CLI::ConfigOutputMode::AllDefaults);
```
```toml
plain.value=1
[configurable]
value=1
```
```cpp
CLI::App app;
app.add_option(...);
// several other options
CLI11_PARSE(app, argc, argv);
//the config printout should be after the parse to capture the given arguments
std::cout<<app.config_to_str(true,true);
```
if a prefix is needed to print before the options, for example to print a config
for just a subcommand, the config formatter can be obtained directly.
```cpp
auto fmtr=app.get_config_formatter();
//std::string to_config(const App *app, bool default_also, bool write_description, std::string prefix)
fmtr->to_config(&app,true,true,"sub.");
//prefix can be used to set a prefix before each argument, like "sub."
```
The formatter also has an overload taking `CLI::ConfigOutputMode`:
```cpp
auto fmtr=app.get_config_formatter();
//std::string to_config(const App *app, CLI::ConfigOutputMode mode, bool write_description, std::string prefix)
fmtr->to_config(&app,CLI::ConfigOutputMode::ActiveSubcommandDefaults,true,"sub.");
```
### Customization of configure file output
The default config parser/generator has some customization points that allow
variations on the TOML format. The default formatter has a base configuration
that matches the TOML format. It defines 5 characters that define how different
aspects of the configuration are handled. You must use
`get_config_formatter_base()` to have access to these fields
```cpp
/// the character used for comments
char commentChar = '#';
/// the character used to start an array '\0' is a default to not use
char arrayStart = '[';
/// the character used to end an array '\0' is a default to not use
char arrayEnd = ']';
/// the character used to separate elements in an array
char arraySeparator = ',';
/// the character used separate the name from the value
char valueDelimiter = '=';
/// the character to use around strings
char stringQuote = '"';
/// the character to use around single characters and literal strings
char literalQuote = '\'';
/// the maximum number of layers to allow
uint8_t maximumLayers{255};
/// the separator used to separator parent layers
char parentSeparatorChar{'.'};
/// comment default values
bool commentDefaultsBool = false;
/// specify the config reader should collapse repeated field names to a single vector
bool allowMultipleDuplicateFields{false};
/// Specify the configuration index to use for arrayed sections
int16_t configIndex{-1};
/// Specify the configuration section that should be used
std::string configSection;
```
These can be modified via setter functions
- `ConfigBase *comment(char cchar)`: Specify the character to start a comment
block
- `ConfigBase *arrayBounds(char aStart, char aEnd)`: Specify the start and end
characters for an array
- `ConfigBase *arrayDelimiter(char aSep)`: Specify the delimiter character for
an array
- `ConfigBase *valueSeparator(char vSep)`: Specify the delimiter between a name
and value
- `ConfigBase *quoteCharacter(char qString, char literalChar)` :specify the
characters to use around strings and single characters
- `ConfigBase *commentDefaults(bool comDef)` : set to true to comment lines with
a default value
- `ConfigBase *allowDuplicateFields(bool value)` :set to true to allow duplicate
fields to be merged even if not sequential
- `ConfigBase *maxLayers(uint8_t layers)` : specify the maximum number of parent
layers to process. This is useful to limit processing for larger config files
- `ConfigBase *parentSeparator(char sep)` : specify the character to separate
parent layers from options
- `ConfigBase *section(const std::string &sectionName)` : specify the section
name to use to get the option values, only this section will be processed
- `ConfigBase *index(int16_t sectionIndex)` : specify an index section to use
for processing if multiple TOML sections of the same name are present
`[[section]]`
For example, to specify reading a configure file that used `:` to separate name
and values:
```cpp
auto config_base=app.get_config_formatter_base();
config_base->valueSeparator(':');
```
The default configuration file will read INI files, but will write out files in
the TOML format. To specify outputting INI formatted files use
```cpp
app.config_formatter(std::make_shared<CLI::ConfigINI>());
```
which makes use of a predefined modification of the ConfigBase class which TOML
also uses. If a custom formatter is used that is not inheriting from the from
ConfigBase class `get_config_formatter_base()` will return a nullptr if RTTI is
on (usually the default), or garbage if RTTI is off, so some care must be
exercised in its use with custom configurations.
## Custom formats
You can invent a custom format and set that instead of the default INI
formatter. You need to inherit from `CLI::Config` and implement the following
two functions:
```cpp
std::string to_config(const CLI::App *app, bool default_also, bool, std::string) const;
std::vector<CLI::ConfigItem> from_config(std::istream &input) const;
```
The `CLI::ConfigItem`s that you return are simple structures with a name, a
vector of parents, and a vector of results. A optionally customizable `to_flag`
method on the formatter lets you change what happens when a ConfigItem turns
into a flag.
Finally, set your new class as new config formatter:
```cpp
app.config_formatter(std::make_shared<NewConfig>());
```
See
[`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp)
for a complete JSON config example.
### Trivial JSON configuration example
```JSON
{
"test": 56,
"testb": "test",
"flag": true
}
```
The parser can handle these structures with only a minor tweak
```cpp
app.get_config_formatter_base()->valueSeparator(':');
```
The open and close brackets must be on a separate line and the comma gets
interpreted as an array separator but since no values are after the comma they
get ignored as well. This will not support multiple layers or sections or any
other moderately complex JSON, but can work if the input file is simple.
## Triggering Subcommands
Configuration files can be used to trigger subcommands if a subcommand is set to
configure. By default configuration file just set the default values of a
subcommand. But if the `configure()` option is set on a subcommand then the if
the subcommand is utilized via a `[subname]` block in the configuration file it
will act as if it were called from the command line. Subsubcommands can be
triggered via `[subname.subsubname]`. Using the `[[subname]]` will be as if the
subcommand were triggered multiple times from the command line. This
functionality can allow the configuration file to act as a scripting file.
For custom configuration files this behavior can be triggered by specifying the
parent subcommands in the structure and `++` as the name to open a new
subcommand scope and `--` to close it. These names trigger the different
callbacks of configurable subcommands.
## Stream parsing
In addition to the regular parse functions a
`parse_from_stream(std::istream &input)` is available to directly parse a stream
operator. For example to process some arguments in an already open file stream.
The stream is fed directly in the config parser so bypasses the normal command
line parsing.
## Implementation Notes
The config file input works with any form of the option given: Long, short,
positional, or the environment variable name. When generating a config file it
will create an option name in following priority.
1. First long name
2. First short name
3. Positional name
4. Environment name
In config files the name will be enclosed in quotes if there is any potential
ambiguities in parsing the name.
+166
View File
@@ -0,0 +1,166 @@
# Adding Flags {#book-flags}
The most basic addition to a command line program is a flag. This is simply
something that does not take any arguments. Adding a flag in CLI11 is done in
one of three ways.
## Boolean flags
The simplest way to add a flag is probably a boolean flag:
```cpp
bool my_flag{false};
app.add_flag("-f", my_flag, "Optional description");
```
This will bind the flag `-f` to the boolean `my_flag`. After the parsing step,
`my_flag` will be `false` if the flag was not found on the command line, or
`true` if it was. The flag is allowed any number of times, and the last value
given wins; bool flags always use `CLI::MultiOptionPolicy::TakeLast` (this is
not inherited from the parent defaults, since allowing repeats is often useful
even if you don't want other options to allow multiple values). If you want
passing something like `./my_app -f -f` or `./my_app -ff` to be an error, use
`->multi_option_policy(CLI::MultiOptionPolicy::Throw)`. A flag name may start
with any character except ('-', ' ', '\n', and '!'). For long flags, after the
first character all characters are allowed except ('=',':','{',' ', '\n'). Names
are given as a comma separated string, with the dash or dashes. A flag can have
as many names as you want, and afterward, using `count`, you can use any of the
names, with dashes as needed.
## Integer flags
If you want to allow multiple flags and count their value, simply use any
integral variables instead of a bool:
```cpp
int my_flag{0};
app.add_flag("-f", my_flag, "Optional description");
```
After the parsing step, `my_flag` will contain the number of times this flag was
found on the command line, including 0 if not found.
This behavior can also be controlled manually via
`->multi_option_policy(CLI::MultiOptionPolicy::Sum)` as of version 2.2.
## Arbitrary type flags
CLI11 allows the type of the variable to assign to in the `add_flag` function to
be any supported type. This is particularly useful in combination with
specifying default values for flags. The allowed types include bool, int, float,
vector, enum, or string-like.
### Default Flag Values
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.
## Pure flags
Every command that starts with `add_`, such as the flag commands, return a
pointer to the internally stored `CLI::Option` that describes your addition. If
you prefer, you can capture this pointer and use it, and that allows you to skip
adding a variable to bind to entirely:
```cpp
CLI::Option* my_flag = app.add_flag("-f", "Optional description");
```
After parsing, you can use `my_flag->count()` to count the number of times this
was found. You can also directly use the value (`*my_flag`) as a bool.
`CLI::Option` will be discussed in more detail later.
## Callback flags
If you want to define a callback that runs when you make a flag, you can use
`add_flag_function` (C++11 or newer) or `add_flag` (C++14 or newer only) to add
a callback function. The function should have the signature
`void(std::int64_t)`. This could be useful for a version printout, etc.
```cpp
auto callback = [](std::int64_t count){std::cout << "This was called " << count << " times";};
app.add_flag_function("-c", callback, "Optional description");
```
## Aliases
The name string, the first item of every `add_` method, can contain as many
short and long names as you want, separated by commas. For example,
`"-a,--alpha,-b,--beta"` would allow any of those to be recognized on the
command line. If you use the same name twice, or if you use the same name in
multiple flags, CLI11 will immediately throw a `CLI::ConstructionError`
describing your problem (it will not wait until the parsing step).
If you want to make an option case-insensitive, you can use the
`->ignore_case()` method on the `CLI::Option` to do that. For example,
```cpp
bool flag{false};
app.add_flag("--flag", flag)
->ignore_case();
```
would allow the following to count as passing the flag:
```text
./my_app --fLaG
```
## Example
The following program will take several flags:
\snippet flags.cpp define
The values would be used like this:
\snippet flags.cpp usage
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/flags.cpp)
If you compile and run:
```text
$ g++ -std=c++11 flags.cpp
$ ./a.out -h
Flag example program
Usage: ./a.out [OPTIONS]
Options:
-h,--help Print this help message and exit
-b,--bool This is a bool flag
-i,--int This is an int flag
-p,--plain This is a plain flag
$ ./a.out -bii --plain -i
The flags program
Bool flag passed
Flag int: 3
Flag plain: 1
```
+167
View File
@@ -0,0 +1,167 @@
# Formatting help output {#book-formatting}
## Customizing an existing formatter
In CLI11, you can control the output of the help printout in full or in part.
The default formatter was written in such a way as to be customizable. You can
use `app.get_formatter()` to get the current formatter. The formatter you set
will be inherited by subcommands that are created after you set the formatter.
There are several configuration options that you can set:
| Set method | Description | Availability |
| ------------------------------------------ | -------------------------------------------------------------------- | ------------ |
| `column_width(width)` | The width of the columns (30) | Both |
| `label(key, value)` | Set a label to a different value | Both |
| `long_option_alignment_ratio(float)` | Set the alignment ratio for long options within the left column(1/3) | Both |
| `right_column_width(std::size_t)` | Set the right column width(65) | Both |
| `description_paragraph_width(std::size_t)` | Set the description paragraph width at the top of help(80) | Both |
| `footer_paragraph_width(std::size_t)` | Set the footer paragraph width (80) | Both |
| `enable_description_formatting(bool)` | enable/disable description paragraph formatting (true) | Both |
| `enable_footer_formatting(bool)` | enable/disable footer paragraph formatting (true) | Both |
| `enable_option_defaults(bool)` | enable/disable printing of option defaults (true) | Both |
| `enable_option_type_names(bool)` | enable/disable printing of option types (true) | Both |
| `enable_default_flag_values(bool)` | enable/disable printing of default flag values (true) | Both |
Labels will map the built in names and type names from key to value if present.
For example, if you wanted to change the width of the columns to 40 and the
`REQUIRED` label from `(REQUIRED)` to `(MUST HAVE)`:
```cpp
app.get_formatter()->column_width(40);
app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
```
Used labels are `REQUIRED`, `POSITIONALS`, `Usage`, `OPTIONS`, `SUBCOMMAND`,
`SUBCOMMANDS`, `Env`, `Needs`,`Excludes`, and any type name such as `TEXT`,
`INT`,`FLOAT` and others. Replacing these labels with new ones will use the
specified words in place of the label.
### Customization Option Descriptions
Some of the control parameters are visualized in Figure 1. They manage the
column widths and ratios of the different sections of the help
![example help output](help_output1.png)
### long option alignment ratio
The long option alignment ratio controls the relative proportion of short to
long option names. It must be a number between 0 and 1. values entered outside
this range are converted into the range by absolute value or inversion. It
defines where in the left column long options are aligned. It is a ratio of the
column width property.
### formatting options
There are occasions where it is necessary to disable the formatting for headers
and footers the two options `enable_description_formatting(false)` and
`enable_footer_formatting(false)` turn off any formatting on the description and
footer. This allows things like word art or external management of alignment and
width. With formatting enabled the width is enforced and the paragraphs
reflowed.
### Option output control
Additional control options manage printing of specific aspects of an option
```text
OPTIONS:
-h, --help Print this help message and exit
--opt TEXT [DEFFFF]
-o, --opt2 INT this is a description for opt2
-f, -n, --opt3, --option-double FLOAT
this is a description for option3
--flag, --no_flag{false}
a flag option with a negative flag as well
```
The `[DEFFFF]` portion, which is the default value for options if specified can
be turned off in the help output through `enable_option_defaults(false)`. The
`TEXT`, `INT`, `FLOAT` or other type names can be turned off via
`enable_option_type_names(false)`. and the `{false}` or flag default values can
be turned off using `enable_default_flag_values(false)`.
An option with a delimiter shows it on the type name, so `--opt TEXT,...` takes
values such as `a,b,c`. A trailing `...` is separate, it means the option also
takes values separated by spaces. The delimiter is part of the type name, so
`enable_option_type_names(false)` hides it as well.
## Subclassing
You can further configure pieces of the code while still keeping most of the
formatting intact by subclassing either formatter and replacing any of the
methods with your own. The formatters use virtual functions most places, so you
are free to add or change anything about them. For example, if you wanted to
remove the info that shows up between the option name and the description:
```cpp
class MyFormatter : public CLI::Formatter {
public:
std::string make_option_opts(const CLI::Option *) const override {return "";}
};
app.formatter(std::make_shared<MyFormatter>());
```
Look at the class definitions in `FormatterFwd.hpp` or the method definitions in
`impl/Formatter_inl.hpp` to see what methods you have access to and how they are
put together.
## Anatomy of a help message
This is a normal printout, with `<>` indicating the methods used to produce each
line.
```text
<make_description(app)>
<make_usage(app, name)>
<make_positionals(app)>
<make_group("POSITIONALS", true, opts)>
<make_groups(app, mode)>
<make_group("Option Group 1", false, opts)>
<make_group("Option Group 2", false, opts)>
...
<make_subcommands(app, mode)>
<make_subcommand(sub1)>
<make_subcommand(sub2)>
<make_footer(app)>
```
`make_usage` calls `make_option_usage(opt)` on all the positionals to build that
part of the line. `make_subcommand` passes the subcommand as the app pointer.
The `make_groups` print the group name then call `make_option(o)` on the options
listed in that group. The normal printout for an option looks like this:
```text
make_option_opts(o)
┌───┴────┐
-n,--name (REQUIRED) This is a description
└────┬────┘ └──────────┬──────────┘
make_option_name(o,p) make_option_desc(o)
```
Notes:
- `o` is opt pointer, `p` is true if positional.
## formatting callback
For certain cases it is useful to use a callback for the help formatting
```c++
app.formatter_fn(
[](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); });
```
This callback replaces the make_help call in the formatter with the callback.
This is a wrapper around a custom formatter that just needs the main call. All
configuration options are available but are ignored as the output is purely
driven by the callback. The first argument is a const pointer to the App 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.
+374
View File
@@ -0,0 +1,374 @@
# Installation {#book-installation}
## Single file edition
```cpp
#include <CLI11.hpp>
```
This example uses the single file edition of CLI11. You can download `CLI11.hpp`
from the latest release and put it into the same folder as your source code,
then compile this with C++ enabled. For a larger project, you can just put this
in an include folder and you are set. This is the simplest and most
straightforward means of including CLI11 with a project.
## Full edition
```cpp
#include <CLI/CLI.hpp>
```
If you want to use CLI11 in its full form, you can also use the original
multiple file edition. This has an extra utility (`Timer`), and is does not
require that you use a release. The only change to your code would be the
include shown above.
### CMake support for the full edition
If you use CMake 3.14+ for your project (highly recommended), CLI11 comes with a
powerful CMakeLists.txt file that was designed to also be used with
`add_subdirectory`. You can add the repository to your code (preferably as a git
submodule), then add the following line to your project (assuming your folder is
called CLI11):
```cmake
add_subdirectory(CLI11)
```
Then, you will have a target `CLI11::CLI11` that you can link to with
`target_link_libraries`. It will provide the include paths you need for the
library. This is the way [GooFit](https://github.com/GooFit/GooFit) uses CLI11,
for example.
You can also configure and optionally install CLI11, and CMake will create the
necessary `lib/cmake/CLI11/CLI11Config.cmake` files, so
`find_package(CLI11 CONFIG REQUIRED)` also works.
If you use conan.io, CLI11 supports that too. CLI11 also supports Meson and
pkg-config if you are not using CMake.
#### Precompiled mode
CLI11 is header-only by default: every function is `inline`, so each translation
unit that includes CLI11 compiles the whole library again. In a large project
that includes CLI11 in many places, this is slow.
Set the CMake option `CLI11_PRECOMPILED` to compile the library one time into a
static library instead:
```bash
cmake -S . -B build -DCLI11_PRECOMPILED=ON
```
The target you link against does not change. `CLI11::CLI11` is a static library
instead of an interface library, and it applies the `CLI11_COMPILE` definition
to your code for you:
```cmake
target_link_libraries(MyTarget PRIVATE CLI11::CLI11)
```
The mechanism is the `CLI11_INLINE` macro. Each public header includes a
matching `CLI/impl/*_inl.hpp` implementation header at the end. Without
`CLI11_COMPILE`, `CLI11_INLINE` expands to `inline` and the implementation is
part of the header. With `CLI11_COMPILE`, `CLI11_INLINE` expands to nothing, the
headers stop including the implementation, and the definitions must come from
somewhere else.
If you do not use CLI11's CMake, you can do the same thing yourself. Define
`CLI11_COMPILE` for all of your code, then compile one source file that includes
the implementation headers:
```cpp
// cli11_impl.cpp - compiled one time
#include <CLI/impl/App_inl.hpp>
#include <CLI/impl/Argv_inl.hpp>
#include <CLI/impl/Config_inl.hpp>
#include <CLI/impl/Encoding_inl.hpp>
#include <CLI/impl/ExtraValidators_inl.hpp>
#include <CLI/impl/Formatter_inl.hpp>
#include <CLI/impl/Option_inl.hpp>
#include <CLI/impl/Split_inl.hpp>
#include <CLI/impl/StringTools_inl.hpp>
#include <CLI/impl/Validators_inl.hpp>
```
This is exactly what `src/Precompile.cpp` does in the CLI11 repository.
Two limits apply. `CLI11_PRECOMPILED` and `CLI11_SINGLE_FILE` are mutually
exclusive, because the single header is header-only by construction. Also, the
`impl` headers must be installed for a precompiled build to be usable from an
install tree; set `CLI11_DISABLE_IMPL_HEADERS_INSTALL` only if you do not need
them.
#### Global Headers
Use `CLI/*.hpp` files stored in a shared folder. You could check out the git
repository to a system-wide folder, for example `/opt/`. With CMake, you could
add to the include path via:
```bash
if(NOT DEFINED CLI11_DIR)
set (CLI11_DIR "/opt/CLI11" CACHE STRING "CLI11 git repository")
endif()
include_directories(${CLI11_DIR}/include)
```
And then in the source code (adding several headers might be needed to prevent
linker errors):
```cpp
#include "CLI/App.hpp"
#include "CLI/Formatter.hpp"
#include "CLI/Config.hpp"
```
#### Global Headers with Target
Configuring and installing the project is required for linking CLI11 to your
project in the same way as you would do with any other external library. With
CMake, this step allows using `find_package(CLI11 CONFIG REQUIRED)` and then
using the `CLI11::CLI11` target when linking. If `CMAKE_INSTALL_PREFIX` was
changed during install to a specific folder like `/opt/CLI11`, then you have to
pass `-DCLI11_DIR=/opt/CLI11` when building your current project. You can also
use [Conan.io](https://conan.io/center/cli11) or
[Hunter](https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html). (These are
just conveniences to allow you to use your favorite method of managing packages;
it's just header only so including the correct path and using C++11 is all you
really need.)
#### Modules
Module support is experimental. To use modules, you must use C++20 or later,
CMake 3.28 or later, and the Ninja or Visual Studio generator (Makefiles do not
work). Build and install CLI11 with `-DCLI11_MODULES=ON`, then link the target
`CLI11::Module` (the older name `CLI11::CLI11_Module` stays supported). The
module library contains the precompiled implementation, so an `import cli11;`
client usually compiles several times faster than one that includes the headers.
Macros such as `CLI11_PARSE` are not available through `import cli11;` — use
`app.parse()` and catch `CLI::ParseError`, or include the headers as well. For
complete programs, including an `import std;` variant, see [Using CLI11 as a
C++20 module](@ref book-modules-example).
#### Using Fetchcontent
If you do not want to add cmake as a submodule or include it with your code the
project can be added using `FetchContent`. This capability requires CMake 3.14+
(or 3.11+ with more work).
An example CMake file would include:
```cmake
include(FetchContent)
FetchContent_Declare(
cli11_proj
QUIET
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
GIT_TAG v2.7.2
)
FetchContent_MakeAvailable(cli11_proj)
# And now you can use it
target_link_libraries(<your project> PRIVATE CLI11::CLI11)
```
And use
```c++
#include <CLI/CLI.hpp>
```
in your project. It is highly recommended that you use the git hash for
`GIT_TAG` instead of a tag or branch, as that will both be more secure, as well
as faster to reconfigure - CMake will not have to reach out to the internet to
see if the tag moved. You can also download just the single header file from the
releases using `file(DOWNLOAD)`.
### Running tests on the full edition
CLI11 has examples and tests that can be accessed using a CMake build on any
platform. Simply build and run ctest to run the 200+ tests to ensure CLI11 works
on your system.
The test build uses Catch2. If Catch2 is not available as a CMake package, CLI11
will download the required Catch2 header during configuration. On systems
without internet access, or where TLS connections to GitHub releases are
blocked, install Catch2 separately or configure with `CLI11_BUILD_TESTS=OFF`
until the dependency is available.
As an example of the build system, the following commands will download and test
CLI11 in a simple Alpine Linux docker container. (Docker is being used to create
a pristine disposable environment; there is nothing special about this
container. Alpine is being used because it is small, modern, and fast. Commands
are similar on any other platform.)
```bash
docker run -it alpine
apk add --no-cache g++ cmake make git
git clone https://github.com/CLIUtils/CLI11.git
cd CLI11
mkdir build
cd build
cmake ..
make
make test
```
For the curious, the CMake options and defaults are listed below. Most options
default to off if CLI11 is used as a subdirectory in another project.
| Option | Description |
| ------------------------------------ | ---------------------------------------------------------------- |
| `CLI11_SINGLE_FILE=OFF` | Build the `CLI11.hpp` file from the sources. Requires Python. |
| `CLI11_PRECOMPILED=OFF` | Generate a precompiled library instead of header-only |
| `CLI11_MODULES=OFF` | Build CLI11 as a module (requires C++20 or later) |
| `CLI11_INSTALL_PACKAGE_TESTS=OFF` | Run tests checking the installation |
| `CLI11_MODULE_TESTS=OFF` | Run a test checking that CLI11 works with modules |
| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well |
| `CLI11_BUILD_DOCS=ON` | Build CLI11 documentation and book |
| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. |
| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries |
| `CLI11_INSTALL=ON` | Install CLI11 to the install folder during the install process |
| `CLI11_FULL_INSTALL=OFF` | Install all CLI11 headers/libraries regardless of other settings |
| `CLI11_FORCE_LIBCXX=OFF` | Use libc++ instead of libstdc++ if building with clang on linux |
| `CLI11_DISABLE_IMPL_HEADERS_INSTALL` | Don't install the impl headers if the CLI11_PRECOMPILED is ON |
| `CLI11_CUDA_TESTS=OFF` | Build the tests with NVCC |
| `CLI11_BUILD_TESTS=ON` | Build the tests. |
| `CLI11_ENABLE_EXTRA_VALIDATORS` | Set to 1 to enable the extra validators, 0 to disable them |
| `CLI11_DISABLE_EXTRA_VALIDATORS` | Set to 1 to disable the extra validators |
The last two options set the macro of the same name on the CLI11 target. See
[Validators](@ref book-validators) for what they control.
## Meson support
### Global Headers from pkg-config
If CLI11 is installed globally, then nothing more than `dependency('CLI11')` is
required. If it installed in a non-default search path, then setting the
`PKG_CONFIG_PATH` environment variable of the `--pkg-config-path` option to
`meson setup` is all that's required.
### Using Meson's subprojects
Meson has a system called
[wraps](https://mesonbuild.com/Wrap-dependency-system-manual.html), which allow
Meson to fetch sources, configure, and build dependencies as part of a main
project. This is the mechanism that Meson recommends for projects to use, as it
allows updating the dependency transparently, and allows packagers to have fine
grained control on the use of subprojects vs system provided dependencies.
Simply run `meson wrap install cli11` to install the `cli11.wrap` file, and
commit it, if desired.
It is also possible to use git submodules. This is generally discouraged by
Meson upstream, but may be appropriate if a project needs to build with multiple
build systems and wishes to share subprojects between them. As long as the
submodule is in the parent project's subproject directory nothing additional is
needed.
## Bazel support
CLI11 is a Bazel module. Add it to your `MODULE.bazel`:
```python
bazel_dep(name = "cli11", version = "2.6.2")
```
Then depend on the `@cli11//:cli11` target:
```python
cc_binary(
name = "my_app",
srcs = ["my_app.cpp"],
deps = ["@cli11//:cli11"],
)
```
The module builds CLI11 in precompiled mode. Bazel 7.4 or later is required.
## Installing cli11 using vcpkg
You can download and install cli11 using the
[vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
```bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install cli11
```
The cli11 port in vcpkg is kept up to date by Microsoft team members and
community contributors. If the version is out of date, please
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
vcpkg repository.
## Installing CLI11 using Conan
You can install pre-built binaries for CLI11 or build it from source using
[Conan](https://conan.io/). Use the following command:
```bash
conan install --requires="cli11/[*]" --build=missing
```
The CLI11 Conan recipe is kept up to date by Conan maintainers and community
contributors. If the version is out of date, please
[create an issue or pull request](https://github.com/conan-io/conan-center-index)
on the ConanCenterIndex repository.
## Special instructions for GCC 8, Some clang, and WASI
If you are using GCC 8 and using it in C++17 mode with CLI11. CLI11 makes use of
the `<filesystem>` header if available, but specifically for this compiler, the
`filesystem` library is separate from the standard library and needs to be
linked separately. So it is available but CLI11 doesn't use it by default.
Specifically `libstdc++fs` needs to be added to the linking list and
`CLI11_HAS_FILESYSTEM=1` has to be defined. Then the filesystem variant of the
Validators could be used on GCC 8. GCC 9+ does not have this issue so the
`<filesystem>` is used by default.
There may also be other cases where a specific library needs to be linked.
Defining `CLI11_HAS_FILESYSTEM=0` which will remove the usage and hence any
linking issue.
In some cases certain clang compilations may require linking against `libc++fs`.
These situations have not been encountered so the specific situations requiring
them are unknown yet.
If building with WASI it is necessary to add the flag
`-lc-printscan-long-double` to the build to allow long double support. See #841
for more details.
## Default system packages on Linux
If you are not worried about latest features or recent bug fixes, you can
install a stable version of CLI11 using:
`sudo apt install libcli11-dev` for Ubuntu, or: `sudo dnf install cli11-devel`
on Fedora/Almalinux.
Then, in your CMake project, just call:
```cmake
find_package(CLI11 CONFIG REQUIRED)
target_link_libraries(MyTarget PRIVATE CLI11::CLI11)
```
and in your C++ file:
```cpp
#include "CLI/App.hpp"
#include "CLI/Formatter.hpp"
#include "CLI/Config.hpp"
int main(int argc, char** argv) {
CLI::App app{"MyApp"};
// Here your flags / options
CLI11_PARSE(app, argc, argv);
}
```
+97
View File
@@ -0,0 +1,97 @@
# CLI11 Internals {#book-internals}
## Callbacks
The library was designed to bind to existing variables without requiring typed
classes or inheritance. This is accomplished through lambda functions.
This looks like:
```cpp
Option* add_option(string name, T &item) {
this->function = [&item](string value){
return lexical_cast(value, item);
}
}
```
Obviously, you can't access `T` after the `add_` method is over. To store the
string representation of the default value, call `capture_default_str()` on the
option (or use `always_capture_default()` to do this for every option). Only the
low-level `add_option` overload taking a raw `callback_t` still accepts a
`defaulted` bool argument.
## Parsing
Parsing follows the following procedure:
1. `_validate`: Make sure the defined options and subcommands are self
consistent.
2. `_parse`: Main parsing routine. See below.
3. `_run_callback`: Run an App callback if present.
The parsing phase is the most interesting:
1. `_parse_single`: Run on each entry on the command line and fill the
options/subcommands.
2. `_process`: Run the procedure listed below.
3. `_process_extras`: This throws an error if needed on extra arguments that
didn't fit in the parse.
The `_process` procedure runs the following steps; each step is recursive and
completes all subcommands before moving to the next step. This ensures that
interactions between options and subcommand options is consistent.
```c++
CLI11_INLINE void App::_process() {
// help takes precedence over other potential errors and config and environment shouldn't be processed if help
// throws
_process_callbacks(CallbackPriority::FirstPreHelp);
_process_help_flags(CallbackPriority::First);
_process_callbacks(CallbackPriority::First);
std::exception_ptr config_exception;
try {
// the config file might generate a FileError but that should not be processed until later in the process
// to allow for help, version and other errors to generate first.
_process_config_file();
// process env shouldn't throw but no reason to process it if config generated an error
_process_env();
} catch(const CLI::FileError &) {
config_exception = std::current_exception();
}
// callbacks and requirements processing can generate exceptions which should take priority
// over the config file error if one exists.
_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
_process_help_flags(CallbackPriority::PreRequirementsCheck);
_process_callbacks(CallbackPriority::PreRequirementsCheck);
_process_requirements();
_process_callbacks(CallbackPriority::NormalPreHelp);
_process_help_flags(CallbackPriority::Normal);
_process_callbacks(CallbackPriority::Normal);
if(config_exception) {
std::rethrow_exception(config_exception);
}
_process_callbacks(CallbackPriority::LastPreHelp);
_process_help_flags(CallbackPriority::Last);
_process_callbacks(CallbackPriority::Last);
}
```
Option callbacks can be executed at many different stages depending on the
priority specified. The default is `Normal` so they will execute after
processing requirements. The default for help and version flags is to execute
`First`. Both can be changed to execute in different steps of the process.
## Exceptions
The library immediately returns a C++ exception when it detects a problem, such
as an incorrect construction or a malformed command line. Errors from config
processing are delayed until after other processing, to give priority to any
help or version flags, or other types of callback errors.
+102
View File
@@ -0,0 +1,102 @@
# Using CLI11 as a C++20 module {#book-modules-example}
Module support is experimental. This page shows two complete programs that use
`import cli11;`. For the build options, targets, and requirements, see [Modules
in the installation chapter](@ref book-installation).
## A complete minimal project
Build and install CLI11 with `-DCLI11_MODULES=ON` first, then use this project:
```cmake
cmake_minimum_required(VERSION 3.28)
project(myapp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(CLI11 REQUIRED)
add_executable(myapp myapp.cpp)
target_link_libraries(myapp PRIVATE CLI11::Module)
```
```cpp
// myapp.cpp
// Keep the includes before the import; GCC rejects the reverse order.
#include <iostream>
#include <string>
import cli11;
int main(int argc, char *argv[]) {
CLI::App app{"MyApp"};
std::string file;
app.add_option("-f,--file", file, "The file name")->required();
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
std::cout << "file=" << file << '\n';
return 0;
}
```
Use the same compiler for the CLI11 library and the application. Macros such as
`CLI11_PARSE` are not available through `import cli11;` — use `app.parse()` and
catch `CLI::ParseError`, or include the headers as well.
## Use with import std
You can combine `import cli11;` with `import std;`. This needs C++23 and a
standard library that ships the `std` module (recent libc++, MSVC, or libstdc++
from GCC 15+). CMake support for `import std` (`CMAKE_CXX_MODULE_STD`, CMake
3.30+) is experimental and is behind the `CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`
gate, so this example invokes clang and libc++ directly:
```cpp
// myapp.cpp
import cli11;
import std;
int main(int argc, char *argv[]) {
CLI::App app{"MyApp"};
std::string file;
app.add_option("-f,--file", file, "The file name")->required();
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
std::println("file={}", file);
return 0;
}
```
```sh
# CLI11 = path to the CLI11 sources, LIBCXX = path that contains std.cppm
# (for example <llvm prefix>/share/libc++/v1)
clang++ -std=c++23 -O2 -Wno-reserved-module-identifier --precompile \
$LIBCXX/std.cppm -o std.pcm
clang++ -std=c++23 -O2 -Wno-reserved-module-identifier -c std.pcm -o std.o
clang++ -std=c++23 -O2 -DCLI11_COMPILE -I $CLI11/include --precompile \
$CLI11/src/modules/CLI11.cppm -o cli11.pcm
clang++ -std=c++23 -O2 -c cli11.pcm -o cli11.o
clang++ -std=c++23 -O2 -DCLI11_COMPILE -I $CLI11/include \
-c $CLI11/src/Precompile.cpp -o impl.o
clang++ -std=c++23 -O2 -fmodule-file=std=std.pcm \
-fmodule-file=cli11=cli11.pcm -c myapp.cpp -o myapp.o
clang++ myapp.o cli11.o impl.o std.o -o myapp
```
With both modules prebuilt, only the last two commands run again when
`myapp.cpp` changes, and they are fast; in our tests the application compiled
several times faster than an equivalent header-only build. Exact times depend on
the compiler and the application.
+142
View File
@@ -0,0 +1,142 @@
# Option groups {#book-option-groups}
The `->group("name")` modifier on an option only changes where the option is
printed in the help. An option _group_ is a stronger tool: it is a real
container that collects options and can carry requirements of its own.
```cpp
auto *format = app.add_option_group("output_format", "formatting type for output");
```
`add_option_group` returns a pointer to the group. The description is optional.
An option group is a specialization of `App`, so everything that works on an app
or a subcommand works on a group as well: `require_option`, `excludes`, `needs`,
`disabled`, callbacks, and nesting.
## Adding options to a group
Options can be created directly on the group, exactly as on an app:
```cpp
std::string filename;
format->add_option("--file", filename, "output file");
format->add_flag("--binary", "write binary output");
```
An option that already exists on the parent app can be moved into the group:
```cpp
auto *opt = app.add_option("--file", filename);
format->add_option(opt);
format->add_options(opt1, opt2, opt3);
```
The option pointers must belong to the parent application of the group, or an
error is generated. A subcommand can be moved in the same way, which removes it
from its parent:
```cpp
format->add_subcommand(subcom_pointer);
```
## Requirements between groups
An app counts a whole option group as a single option for the purpose of
`require_option`, and the group counts as used if any option or subcommand
inside it was used. That is what makes groups useful: you can require one of
three options in one group and one of three in another.
```cpp
CLI::App app("data output specification");
auto *format = app.add_option_group("output_format", "formatting type for output");
format->add_flag("--csv", "write csv output");
format->add_flag("--binary", "write binary output");
format->require_option(1); // exactly one of the two
auto *target = app.add_option_group("output_target", "target for the output");
target->add_option("--file", filename, "output file");
target->add_flag("--stdout", "write to stdout");
target->require_option(1);
```
`require_option(N)` requires exactly `N` if `N` is positive, up to `N` if `N` is
negative, and resets to "0 or more" for `N=0`. `require_option(min, max)` sets
both ends, with a `max` of 0 meaning unlimited. `require_option()` with no
argument requires 1 or more.
Disabling a group with `->disabled()` turns off every option inside it. Groups
can also contain other groups.
See the
[option_groups.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp)
and
[ranges.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp)
examples.
## Triggering one group from another
`CLI::TriggerOn` and `CLI::TriggerOff` connect groups, so that use of one group
enables or disables another:
```cpp
CLI::TriggerOn(group1_pointer, triggered_group);
CLI::TriggerOff(group2_pointer, disabled_group);
```
The second argument can also be a `std::vector<App *>`. These helpers are built
from `preparse_callback`, `enabled_by_default()`, and `disabled_by_default()`;
see [Subcommands](@ref book-subcommands). Use them one time per group, since
they replace any earlier use of those three functions. For anything more
complex, write the `preparse_callback` yourself.
## Group names and the help output
The group name controls how the help print treats the group:
- A normal name prints the options under that heading.
- An empty name hides the whole group, which is a quick way to hide a set of
options: `app.add_option_group("")`.
- A name that starts with `+` prints the options as if they were not in a group
at all, and `get_options` treats them the same way:
`app.add_option_group("+sub")`.
Group names may not contain newlines or null characters.
## Nameless subcommands
A subcommand with an empty name behaves much like an option group:
```cpp
auto *shared = app.add_subcommand();
shared->add_option("--shared", value);
```
If an option is not found in the main app, all nameless subcommands are searched
as well. Because `add_subcommand` also accepts a `std::shared_ptr<App>`, a set
of options can be defined in one component and merged into one or more apps.
Multiple nameless subcommands are allowed, and their callbacks run only if some
option from the group was parsed.
## Deprecating and retiring options
Two helpers manage the life cycle of an option that is on its way out:
```cpp
CLI::deprecate_option(option_pointer, "replacement_name");
CLI::deprecate_option(app, "--option_name", "replacement_name");
```
A deprecated option keeps working. The help print marks it, and the first use
prints a warning that names the replacement, if you gave one.
```cpp
CLI::retire_option(app, option_pointer);
CLI::retire_option(app, "--option_name");
```
A retired option does nothing. If the option exists it is replaced with a dummy
option that takes the same arguments, so old command lines still parse, and the
first use prints a warning. See the
[retired.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/retired.cpp)
example.
+575
View File
@@ -0,0 +1,575 @@
# Options {#book-options}
## Simple options
The most versatile addition to a command line program is an option. This is like
a flag, but it takes an argument. CLI11 handles all the details for many types
of options for you, based on their type. To add an option:
```cpp
int int_option{0};
app.add_option("-i", int_option, "Optional description");
```
This will bind the option `-i` to the integer `int_option`. On the command line,
a single value that can be converted to an integer will be expected. Non-integer
results will fail. If that option is not given, CLI11 will not touch the initial
value. This allows you to set up defaults by simply setting your value
beforehand. If you want CLI11 to display your default value, you can add
`->capture_default_str()` after the option.
```cpp
int int_option{0};
app.add_option("-i", int_option, "Optional description")->capture_default_str();
```
You can use any C++ int-like type, not just `int`. CLI11 understands the
following categories of types:
| Type | CLI11 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| number like | Integers, floats, bools, or any type that can be constructed from an integer or floating point number. Accepts common numerical strings like `0xFF` as well as octal (`0755` or `0o755`), decimal, and binary(0b011111100), supports value separators including `_` and `'` |
| string-like | std::string, or anything that can be constructed from or assigned a std::string |
| char | For a single char, single string values are accepted, otherwise longer strings are treated as integral values and a conversion is attempted |
| complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" |
| enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) |
| container-like | a container(like vector) of any available types including other containers |
| wrapper | any other object with a `value_type` static definition where the type specified by `value_type` is one of the type in this list, including `std::atomic<>` |
| tuple | a tuple, pair, or array, or other type with a tuple size and tuple_type operations defined and the members being a type contained in this list |
| function | A function that takes an array of strings and returns a string that describes the conversion failure or empty for success. May be the empty function. (`{}`) |
| streamable | any other type with a `<<` operator will also work |
By default, CLI11 will assume that an option is optional, and one value is
expected if you do not use a vector. You can change this on a specific option
using option modifiers. An option name may start with any character except ('-',
' ', '\n', and '!'). For long options, after the first character all characters
are allowed except ('=',':','{',' ', '\n'). Names are given as a comma separated
string, with the dash or dashes. An option 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.
## Positional options and aliases
When you give an option on the command line without a name, that is a positional
option. Positional options are accepted in the same order they are defined. So,
for example:
```text
./a.out one --two three four
```
The string `one` would have to be the first positional option. If `--two` is a
flag, then the remaining two strings are positional. If `--two` is a
one-argument option, then `four` is the second positional. If `--two` accepts
two or more arguments, then there are no more positionals.
To make a positional option, you simply give CLI11 one name that does not start
with a dash. You can have as many (non-overlapping) names as you want for an
option, but only one positional name. So the following name string is valid:
```cpp
"-a,-b,--alpha,--beta,mypos"
```
This would make two short option aliases, two long option alias, and the option
would be also be accepted as a positional.
## Containers of options
If you use a vector or other container instead of a plain option, you can accept
more than one value on the command line. By default, a container accepts as many
options as possible, until the next value that could be a valid option name. You
can specify a set number using an option modifier `->expected(N)`. (The default
unlimited behavior on vectors is restored with `N=-1`) CLI11 does not
differentiate between these two methods for unlimited acceptance options.
| Separate names | Combined names |
| ----------------- | -------------- |
| `--vec 1 --vec 2` | `--vec 1 2` |
It is also possible to specify a minimum and maximum number through
`->expected(Min,Max)`. It is also possible to specify a min and max type size
for the elements of the container. It most cases these values will be
automatically determined but a user can manually restrict them.
An example of setting up a vector option:
```cpp
std::vector<int> int_vec;
app.add_option("--vec", int_vec, "My vector option");
```
Vectors will be replaced by the parsed content if the option is given on the
command line.
A definition of a container for purposes of CLI11 is a type with a `end()`,
`insert(...)`, `clear()` and `value_type` definitions. This includes `vector`,
`set`, `deque`, `list`, `forward_list`, `map`, `unordered_map` and a few others
from the standard library, and many other containers from the boost library.
### Empty containers
By default a container will never return an empty container. If it is desired to
allow an empty container to be returned, then the option must be modified with a
0 as the minimum expected value
```cpp
std::vector<int> int_vec;
app.add_option("--vec", int_vec, "Empty vector allowed")->expected(0,-1);
```
An empty vector can than be specified on the command line as `--vec {}`
To allow an empty vector from config file, the default must be set in addition
to the above modification.
```cpp
std::vector<int> int_vec;
app.add_option("--vec", int_vec, "Empty vector allowed")->expected(0,-1)->default_str("{}");
```
Then in the file
```toml
vec={}
```
or
```toml
vec=[]
```
will generate an empty vector in `int_vec`.
### Containers of containers
Containers of containers are also supported.
```cpp
std::vector<std::vector<int>> int_vec;
app.add_option("--vec", int_vec, "My vector of vectors option");
```
CLI11 inserts a separator sequence at the start of each argument call to
separate the vectors. So unless the separators are injected as part of the
command line each call of the option on the command line will result in a
separate element of the outer vector. This can be manually controlled via
`inject_separator(true|false)` but in nearly all cases this should be left to
the defaults. To insert of a separator from the command line add a `%%` where
the separation should occur.
```bash
cmd --vec 1 2 3 4 %% 1 2
```
would then result in a container of size 2 with the first element containing 4
values and the second 2.
This separator is also the only way to get values into something like
```cpp
std::pair<std::vector<int>,std::vector<int>> two_vecs;
app.add_option("--vec", two_vecs, "pair of vectors");
```
without calling the argument twice.
Further levels of nesting containers should compile but intermediate layers will
only have a single element in the container, so is probably not that useful.
### Nested types
Types can be nested. For example:
```cpp
std::map<int, std::pair<int,std::string>> map;
app.add_option("--dict", map, "map of pairs");
```
will require 3 arguments for each invocation, and multiple sets of 3 arguments
can be entered for a single invocation on the command line.
```cpp
std::map<int, std::pair<int,std::vector<std::string>>> map;
app.add_option("--dict", map, "map of pairs");
```
will result in a requirement for 2 integers on each invocation and absorb an
unlimited number of strings including 0.
## Option modifiers
When you call `add_option`, you get a pointer to the added option. You can use
that to add option modifiers. A full listing of the option modifiers:
| Modifier | Description |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `->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, mainly for vector args. |
| `->expected(Nmin,Nmax)` | Take between `Nmin` and `Nmax` values. |
| `->type_size(N)` | specify that each block of values would consist of N elements |
| `->type_size(Nmin,Nmax)` | specify that each block of values would consist of between Nmin and Nmax elements |
| `->needs(opt)` | This option requires another option to also be present, opt is an `Option` pointer or a string with the name of the option. Can be removed with `->remove_needs(opt)` |
| `->excludes(opt)` | This option cannot be given with `opt` present, opt is an `Option` pointer or a string with the name of the option. Can be removed with `->remove_excludes(opt)` |
| `->envname(name)` | Gets the value from the environment if present and not passed on the command line and passes any validators. |
| `->group(name)` | The help group to put the option in. No effect for positional options. Defaults to `"Options"`. Options given an empty string for the group name will not show up in the help print. |
| `->description(string)` | Set/change the description |
| `->ignore_case()` | Ignore the case on the command line (also works on subcommands, does not affect arguments). |
| `->ignore_underscore()` | Ignore any underscores on the command line (also works on subcommands, does not affect arguments). |
| `->allow_extra_args()` | Allow extra argument values to be included when an option is passed. Enabled by default for vector options. Use `->allow_extra_args(false)` to take only the argument values the type requires, which keeps the arguments that follow available for positionals. |
| `->disable_flag_override()` | specify that flag options cannot be overridden on the command line use `=<newval>` |
| `->delimiter('<CH>')` | specify a character that can be used to separate elements in a command line argument, default is `<none>`, common values are ',', and ';'. A delimiter does not limit the option to a single argument; combine it with `->allow_extra_args(false)` for that. |
| `->multi_option_policy( CLI::MultiOptionPolicy::Throw)` | Sets the policy for handling multiple arguments if the option was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`, `Join`, `Reverse`, and `Sum` are also available. See the next four lines for shortcuts to set this more easily. |
| `->take_last()` | Only use the last option if passed several times. This is always true by default for bool options, regardless of the app default, but can be set to false explicitly with `->multi_option_policy()`. |
| `->take_first()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst)` |
| `->take_all()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeAll)` |
| `->join()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses newlines or the specified delimiter to join all arguments into a single string output. |
| `->join(delim)` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses `delim` to join all arguments into a single string output. this also sets the delimiter |
| `->check(Validator)` | perform a check on the returned results to verify they meet some criteria. See [Validators](@ref book-validators) for more info |
| `->transform(Validator)` | Run a transforming validator on each value passed. See [Validators](@ref book-validators) for more info |
| `->each(void(std::string))` | Run a function on each parsed value, _in order_. |
| `->default_str(string)` | set a default string for use in the help and as a default value if no arguments are passed and a value is requested |
| `->default_function(std::string())` | Advanced: Change the function that `capture_default_str()` uses. |
| `->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). |
| `->capture_default_str()` | Store the current value attached and display it in the help string. |
| `->always_capture_default()` | Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`. |
| `->run_callback_for_default()` | Force the option callback to be executed or the variable set when the `default_val` is used. |
| `->force_callback()` | Force the option callback to be executed regardless of whether the option was used or not. Will use the default_str if available, if no default is given the callback will be executed with an empty string as an argument, which will translate to a default initialized value, which can be compiler dependent |
| `->trigger_on_parse()` | Have the option callback be triggered when the value is parsed vs. at the end of all parsing, the option callback can potentially be executed multiple times. Generally only useful if you have a user defined callback or validation check. Or potentially if a vector input is given multiple times as it will clear the results when a repeat option is given via command line. It will trigger the callbacks once per option call on the command line |
| `->option_text(string)` | Sets the text between the option name and description. |
The `->check(...)` and `->transform(...)` modifiers can also take a callback
function that runs on every value that the option receives and returns an error
message as a `std::string`; an empty string means the value passed. A `check`
function receives the value by `const` reference, while a `transform` function
may modify it.
### Multi Option policy
The Multi option policy can be used to instruct CLI11 what to do when an option
is called multiple times and how to return those values in a meaningful way.
There are several options can be set through the
`->multi_option_policy( CLI::MultiOptionPolicy::Throw)` option modifier.
`Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`,
`Join`, `Reverse`, and `Sum`
| Value | Description |
| --------- | --------------------------------------------------------------------------------- |
| Throw | Throws an error if more values are given then expected |
| TakeLast | Selects the last expected number of values given |
| TakeFirst | Selects the first expected number of values given |
| Join | Joins the strings together using the `delimiter` given |
| TakeAll | Takes all the values |
| Sum | If the values are numeric, it sums them and returns the result |
| Reverse | Selects the last expected number of values given and return them in reverse order |
NOTE: For reverse, the index used for an indexed validator (a validator with an
[application index](@ref book-validators)) is also applied in reverse order
index 1 will be the last element and 2 second from last and so on.
## Using the `CLI::Option` pointer
Each of the option creation mechanisms returns a pointer to the internally
stored option. If you save that pointer, you can continue to access the option,
and change setting on it later. The Option object can also be converted to a
bool to see if it was passed, or `->count()` can be used to see how many times
the option was passed. Since flags are also options, the same methods work on
them.
```cpp
CLI::Option* opt = app.add_flag("--opt");
CLI11_PARSE(app, argc, argv);
if(* opt)
std::cout << "Flag received " << opt->count() << " times." << '\n';
```
## Getting results without a bound variable
Binding a variable or a callback in the `add_*` call is the fastest way to get a
result, and is what you should reach for first. When that is not possible, the
values can be pulled back out of the option. These calls do the type conversion
and processing at the point of the call, so keep them out of performance
critical code.
```cpp
CLI::Option *opt = app.add_option("--opt");
CLI11_PARSE(app, argc, argv);
std::vector<std::string> raw = opt->results(); // every value, in order
int value = opt->as<int>(); // converted, one value
std::vector<int> values = opt->as<std::vector<int>>(); // converted, all values
opt->results(value); // same as as<int>(), without a copy
```
`as<T>()` and `results(T&)` apply the multi option policy for a single value,
and return everything for a vector type. If you know that the results will be
needed as a vector, tell CLI11 when you create the option, with
`->expected(CLI::detail::expected_max_vector_size)` or `->allow_extra_args()`.
You do not need to keep the pointer. `app["--opt"]` and
`app.get_option("--opt")` find it again by any of its names:
```cpp
int value = app["--opt"]->as<int>();
```
## Options with a callback
Instead of a variable, an option can take a function. `add_option_function<T>`
converts the arguments to `T` first and hands you the finished value:
```cpp
app.add_option_function<int>("--count", [](int count) {
std::cout << "got " << count << '\n';
});
```
The type argument is required, since it is what tells CLI11 how to parse. This
pairs well with `->trigger_on_parse()` when the callback has to run while
parsing rather than at the end; see the
[custom_validator.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/custom_validator.cpp)
example. `add_flag_function` is the flag counterpart, and takes a
`void(std::int64_t)` function. For full control over the raw strings, see
[Custom option callbacks](@ref book-advanced-topics).
## Inheritance of defaults
One of CLI11's systems to allow customizability without high levels of verbosity
is the inheritance system. You can set default values on the parent `App`, and
all options and subcommands created from it remember the default values at the
point of creation. The default value for Options, specifically, are accessible
through the `option_defaults()` method. There are a number of settings that can
be set and inherited:
- `group`: The group name starts as "Options"
- `required`: If the option must be given. Defaults to `false`. Is ignored for
flags.
- `multi_option_policy`: What to do if several copies of an option are passed
and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This
is also used for bool flags, but they always are created with the value
`CLI::MultiOptionPolicy::TakeLast` or `CLI::MultiOptionPolicy::Sum` regardless
of the default, so that multiple bool flags does not cause an error. But you
can override that setting by calling the `multi_option_policy` directly.
- `ignore_case`: Allow any mixture of cases for the option or flag name
- `ignore_underscore`: Allow any number of underscores in the option or flag
name
- `configurable`: Specify whether an option can be configured through a config
file
- `disable_flag_override`: do not allow flag values to be overridden on the
command line
- `always_capture_default`: specify that the default values should be
automatically captured.
- `delimiter`: A delimiter to use for capturing multiple values in a single
command line string (e.g. --flag="flag,-flag2,flag3")
An example of usage:
```cpp
app.option_defaults()->ignore_case()->group("Required");
CLI::Option* opt = app.add_flag("--CaSeLeSs");
opt->get_group() // is "Required"
```
Groups are mostly for visual organization, but an empty string for a group name
will hide the option.
### Windows style options
You can also set the app setting `app->allow_windows_style_options()` to allow
windows style options to also be recognized on the command line:
- `/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. You still specify option
names in the same manner as on Linux with single and double dashes when you use
the `add_*` functions, and the Linux style on the command line will still work.
If a long and a short option share the same name, the option will match on the
first one defined.
## Parse configuration
How an option and its arguments are parsed depends on a set of controls that are
part of the option structure. In most circumstances these controls are set
automatically based on the type or function used to create the option and the
type the arguments are parsed into. The variables define the size of the
underlying type (essentially how many strings make up the type), the expected
size (how many groups are expected) and a flag indicating if multiple groups are
allowed with a single option. And these interact with the `multi_option_policy`
when it comes time to parse.
### Examples
How options manage this is best illustrated through some examples.
```cpp
std::string val;
app.add_option("--opt",val,"description");
```
creates an option that assigns a value to a `std::string` When this option is
constructed it sets a type_size min and max of 1. Meaning that the assignment
uses a single string. The Expected size is also set to 1 by default, and
`allow_extra_args` is set to false. meaning that each time this option is called
1 argument is expected. This would also be the case if val were a `double`,
`int` or any other single argument types. The modifier `allow_extra_args` should
be set to true if the option output will ever be needed as a vector.
now for example
```cpp
std::pair<int, std::string> val;
app.add_option("--opt",val,"description");
```
In this case the typesize is automatically detected to be 2 instead of 1, so the
parsing would expect 2 arguments associated with the option.
```cpp
std::vector<int> val;
app.add_option("--opt",val,"description");
```
detects a type size of 1, since the underlying element type is a single string,
so the minimum number of strings is 1. But since it is a vector the expected
number can be very big. The default for a vector is (1<<30), and the
allow_extra_args is set to true. This means that at least 1 argument is expected
to follow the option, but arbitrary numbers of arguments may follow. These are
checked if they have the form of an option but if not they are added to the
argument.
```cpp
std::vector<std::tuple<int, double, std::string>> val;
app.add_option("--opt",val,"description");
```
gets into the complicated cases where the type size is now 3. and the expected
max is set to a large number and `allow_extra_args` is set to true. In this case
at least 3 arguments are required to follow the option, and subsequent groups
must come in groups of three, otherwise an error will result.
```cpp
bool val{false};
app.add_flag("--opt",val,"description");
```
Using the add_flag methods for creating options creates an option with an
expected size of 0, implying no arguments can be passed.
```cpp
std::complex<double> val;
app.add_option("--opt",val,"description");
```
triggers the complex number type which has a min of 1 and max of 2, so 1 or 2
strings can be passed. Complex number conversion supports arguments of the form
"1+2j" or "1","2", or "1" "2i". The imaginary number symbols `i` and `j` are
interchangeable in this context.
```cpp
std::vector<std::vector<int>> val;
app.add_option("--opt",val,"description");
```
has a type size of 1 to (1<<30).
### Customization
The `type_size(N)`, `type_size(Nmin, Nmax)`, `expected(N)`,
`expected(Nmin,Nmax)`, and `allow_extra_args()` can be used to customize an
option. For example
```cpp
std::string val;
auto opt=app.add_flag("--opt{vvv}",val,"description");
opt->expected(0,1);
```
will create a hybrid option, that can exist on its own in which case the value
"vvv" is used or if a value is given that value will be used.
A container option takes as many argument values as it can, even if a delimiter
is set. Set `allow_extra_args(false)` to take only one argument value, which is
then split on the delimiter:
```cpp
std::vector<std::string> val;
app.add_option("--opt",val)->delimiter(',')->allow_extra_args(false);
```
`--opt a,b file` now gives `{"a", "b"}` to `--opt` and leaves `file` for a
positional. Without `allow_extra_args(false)`, `file` becomes a third element of
`val`.
There are some additional options that can be specified to modify an option for
specific cases:
- `->run_callback_for_default()` will specify that the callback should be
executed when a default_val is set. This is set automatically when appropriate
though it can be turned on or off and any user specified callback for an
option will be executed when the default value for an option is set.
- `->force_callback()` will for the callback/value assignment to run at the
conclusion of parsing regardless of whether the option was supplied or not.
This can be used to force the default or execute some code.
- `->trigger_on_parse()` will trigger the callback or value assignment each time
the argument is passed. The value is reset if the option is supplied multiple
times.
- `->callback_priority(CallbackPriority priority)`: changes the order in which
the option callback is executed. Four principal callback call-points are
available. `CallbackPriority::First` executes at the very beginning of
processing, before configuration files are read and environment variables are
interpreted. `CallbackPriority::PreRequirementsCheck` executes after
configuration and environment processing but before requirements checking.
`CallbackPriority::Normal` executes after the requirements check but before
any previously potentially raised exceptions are re-thrown.
`CallbackPriority::Last` executes after exception handling is completed. For
each position, both ordinary option callbacks and help callbacks are invoked.
The relative order between them can be controlled using the corresponding
`PreHelp` variants. `CallbackPriority::FirstPreHelp` executes ordinary option
callbacks before help callbacks at the very beginning of processing.
`CallbackPriority::PreRequirementsCheckPreHelp` executes ordinary option
callbacks before help callbacks after configuration and environment processing
but before requirements checking. `CallbackPriority::NormalPreHelp` executes
ordinary option callbacks before help callbacks after the requirements check
but before exception re-throwing. `CallbackPriority::LastPreHelp` executes
ordinary option callbacks before help callbacks after exception handling has
completed. When using the standard priorities (`CallbackPriority::First`,
`CallbackPriority::PreRequirementsCheck`, `CallbackPriority::Normal`,
`CallbackPriority::Last`), help callbacks are executed before ordinary option
callbacks. By default, help callbacks use `CallbackPriority::First`, and
ordinary option callbacks use `CallbackPriority::Normal`. This mechanism
provides fine-grained control over when option values are set and when help or
requirement checks occur, enabling precise customization of the processing
sequence.
## Unusual circumstances
There are a few cases where some things break down in the type system managing
options and definitions. Using the `add_option` method defines a lambda function
to extract a default value if required. In most cases this is either
straightforward or a failure is detected automatically and handled. But in a few
cases a streaming template is available that several layers down may not
actually be defined. This results in CLI11 not being able to detect this
circumstance automatically and will result in compile error. One specific known
case is `boost::optional` if the boost optional_io header is included. This
header defines a template for all boost optional values even if they do not
actually have a streaming operator. For example `boost::optional<std::vector>`
does not have a streaming operator but one is detected since it is part of a
template. For these cases a secondary method `app->add_option_no_stream(...)` is
provided that bypasses this operation completely and should compile in these
cases.
+355
View File
@@ -0,0 +1,355 @@
# Subcommands and the App {#book-subcommands}
Subcommands are keyword 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. This chapter will
focus on implementations that are contained in the same C++ application, though
the system git uses to extend the main command by calling other commands in
separate executables is supported too; that's called "Prefix commands" and is
included at the end of this chapter.
## The parent App
We'll start by discussing the parent `App`. You've already used it quite a bit,
to create options and set option defaults. There are several other things you
can do with an `App`, however.
You are given a lot of control the help output. You can set a footer with
`app.footer("My Footer")`. You can replace the default help print when a
`ParseError` is thrown with `app.failure_message(CLI::FailureMessage::help)`.
The default is `CLI::FailureMessage::simple`, and you can easily define a new
one. Just make a (lambda) function that takes an App pointer and a reference to
an error code (even if you don't use them), and returns a string.
### Inspecting the app
An app can be queried after, or during, the parse:
- `app.get_option("--name")` returns the option pointer, and throws
`CLI::OptionNotFound` if there is none. `app["--name"]` is the same thing on a
const app, and `get_option_no_throw` returns `nullptr` instead of throwing.
- `app.get_options()` returns all options; `app.get_subcommands()` returns the
subcommands that were parsed, in order.
- `app.count("--name")` counts one option, and `app.count_all()` counts every
option and subcommand use in the app.
- `app.parse_order()` returns the options in the order they appeared on the
command line, which is how you recover the relative order of two different
options. See the
[inter_argument_order.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp)
example.
- `app.get_parent()` returns the parent app of a subcommand, or `nullptr` for
the main app.
Options and subcommands can also be taken back out, which is mostly useful when
you build an app from reusable pieces:
```cpp
app.remove_option(opt_pointer);
app.remove_subcommand(sub_pointer);
```
Both return `true` if the item was found and removed.
## Adding a subcommand
Subcommands can be added just like an option:
```cpp
CLI::App* sub = app.add_subcommand("sub", "This is a subcommand");
```
The subcommand should have a name as the first argument, and a little
description for the second argument. A pointer to the internally stored
subcommand is provided; you usually will be capturing that pointer and using it
later (though you can use callbacks if you prefer). As always, feel free to use
`auto sub = ...` instead of naming the type.
You can check to see if the subcommand was received on the command line several
ways:
```cpp
if(*sub) ...
if(sub->parsed()) ...
if(app.got_subcommand(sub)) ...
if(app.got_subcommand("sub")) ...
```
You can also get a list of subcommands with `get_subcommands()`, and they will
be in parsing order.
There are a lot of options that you can set on a subcommand; in fact,
subcommands have exactly the same options as your main app, since they are
actually the same class of object (as you may have guessed from the type above).
This has the pleasant side affect of making subcommands infinitely nestable.
## Required subcommands
Each App has controls to set the number of subcommands you expect. This is
controlled by:
```cpp
app.require_subcommand(/* min */ 0, /* max */ 1);
```
If you set the max to 0, CLI11 will allow an unlimited number of subcommands.
After the (non-unlimited) maximum is reached, CLI11 will stop trying to match
subcommands. So the if you pass "`one two`" to a command, and both `one` and
`two` are subcommands, it will depend on the maximum number as to whether the
"`two`" is a subcommand or an argument to the "`one`" subcommand.
As a shortcut, you can also call the `require_subcommand` method with one
argument; that will be the fixed number of subcommands if positive, it will be
the maximum number if negative. Calling it without an argument will set the
required subcommands to 1 or more.
The maximum number of subcommands is inherited by subcommands. This allows you
to set the maximum to 1 once at the beginning on the parent app if you only want
single subcommands throughout your app. You should keep this in mind, if you are
dealing with lots of nested subcommands.
## Using callbacks
You've already seen how to check to see what subcommands were given. It's often
much easier, however, to just define the code you want to run when you are
making your parser, and not run a bunch of code after `CLI11_PARSE` to analyse
the state (Procedural! Yuck!). You can do that with lambda functions. A
`std::function<void()>` callback `.callback()` is provided, and CLI11 ensures
that all options are prepared and usable by reference capture before entering
the callback. An example is shown below in the `geet` program.
### The three callbacks
`.callback()` sets the final callback. There are three call points in total, and
each one answers a different need:
| Callback | When it runs |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `preparse_callback(f)` | One time, after the first argument of the app or subcommand is seen. `f` takes the number of arguments left to process. |
| `parse_complete_callback(f)` | As soon as the subcommand finishes parsing. Runs one time per use, so it can run several times. |
| `final_callback(f)` | One time, after all processing is complete. This is what `callback()` sets. |
The order for a whole app is: every subcommand `parse_complete_callback`, then
the main app `parse_complete_callback`, then the used subcommand
`final_callback`s, then the option group `final_callback`s, and last the main
app `final_callback`.
Configuration files matter here. A `parse_complete_callback` on a named
subcommand sees no data from a config file, because it runs first. A
`final_callback` runs after config processing. For option groups the
`parse_complete_callback` runs after the config file is read.
A subcommand is finished, and so its `parse_complete_callback` fires, when any
of these happen:
1. There are no more arguments.
2. Another subcommand appears that does not fit an optional positional slot.
3. The positional mark `--` appears and no positional slots are left.
4. The subcommand terminator `++` appears.
Calling a subcommand a second time resets its options and can trigger the
callback again. `.immediate_callback()` is the shorthand for moving the callback
you set with `callback()` to the parse-complete point.
### Triggering subcommands from the command line
A subcommand option can be given without entering the subcommand, using dot
notation:
```text
--sub.long=val
--sub.long val
--sub.f val
--sub1.subsub.f val
```
`--sub.long <args>` is the same as `sub --long <args> ++`, where `++` closes the
subcommand again. The names may be quoted following the TOML rules, which is how
you reach a subcommand whose name contains dots:
`"subcommand.with.dots".arg1 = value`.
## Inheritance of defaults
The following values are inherited when you add a new subcommand. This happens
at the point the subcommand is created:
- The name and description for the help flag
- The footer
- The usage
- The failure message printer function
- The formatter
- The config formatter
- Option defaults
- Allow extras
- Allow config extras
- Prefix command
- Immediate callback
- Ignore case
- Ignore underscore
- Allow Windows style options
- Fallthrough
- Group name
- Max required subcommands
- prefix_matching
- Configurable
- validate positional arguments
- validate optional arguments
## More subcommand modifiers
Some further modifiers apply to an app, a subcommand, or an option group:
| Modifier | Description |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `->require_option()` | Require 1 or more options or option groups. Also `require_option(N)` and `require_option(min, max)`, like `require_subcommand`. |
| `->needs(opt_or_sub)` | The given option or subcommand must be used before this subcommand passes validation. |
| `->excludes(opt_or_sub)` | The given option or subcommand cannot be used together with this one. |
| `->disabled()` | Turn the subcommand off. Takes an optional bool. |
| `->disabled_by_default()` | Disable at the start of each parse, so another subcommand can turn it on. |
| `->enabled_by_default()` | Enable at the start of each parse, so another subcommand can turn it off. |
| `->positionals_at_end()` | Positional arguments must come after all options. |
| `->configurable()` | Allow the subcommand to be triggered from a configuration file. By default config file entries only update defaults. |
| `->alias(name)` | Add another name for the subcommand. |
| `->group(name)` | Set the help group for the subcommand. An empty name hides it from the help. |
| `->allow_non_standard_option_names()` | Accept long option names with a single dash, such as `-single`. Not recommended, but useful when you reproduce an existing interface. A short option may not share its first character with a single dash long name. |
`disabled_by_default` and `enabled_by_default` are the building blocks behind
`CLI::TriggerOn` and `CLI::TriggerOff`, described in @ref book-option-groups.
## Help and version flags
Every app is created with a help flag. You can rename it, replace it, or remove
it:
```cpp
app.set_help_flag("-h,--help", "Print this help message and exit");
app.set_help_flag(); // pass nothing to remove it
```
`set_help_all_flag` adds a second flag that expands every subcommand in the help
output:
```cpp
app.set_help_all_flag("--help-all", "Expand all help");
```
A version flag is not created for you. Add one with a fixed string, or with a
callback that produces the string when the flag is used:
```cpp
app.set_version_flag("--version", std::string(CLI11_VERSION));
app.set_version_flag("--version", []() { return my_version_string(); });
```
All three functions return the option pointer, so the usual option modifiers
apply, and all three replace the existing flag if there is one. The pointers can
be read back later with `get_help_ptr()`, `get_help_all_ptr()`, and
`get_version_ptr()`.
## Special modes
There are several special modes for Apps and Subcommands.
### Allow extras
Normally CLI11 throws an error if you don't match all items given on the command
line. However, you can enable `allow_extras()` to instead store the extra values
in `.remaining()`. You can get all remaining options including those in
contained subcommands recursively in the original order with `.remaining(true)`.
`.remaining_size()` is also provided; this counts the size but ignores the `--`
special separator if present.
### Fallthrough
Fallthrough allows an option that does not match in a subcommand to "fall
through" to the parent command; if that parent allows that option, it matches
there instead. This was added to allow CLI11 to represent models:
```text
./my_program my_model_1 --model_flag --shared_flag
```
Here, `--shared_flag` was set on the main app, and on the command line it "falls
through" `my_model_1` to match on the main app. This is set through
`->fallthrough()` on a subcommand.
calling help on subcommands with fallthrough will result in the parent options
showing as if they were part of the subcommand.
#### Subcommand fallthrough
Subcommand fallthrough allows additional subcommands to be triggered after the
first subcommand. By default subcommand fallthrough is enabled, but it can be
turned off through `->subcommand_fallthrough(false)` on a subcommand. This will
prevent additional subcommands at the same inheritance level from triggering,
the strings would then be treated as positional values. As a technical note if
fallthrough is enabled but subcommand fallthrough disabled (this is not the
default in both cases), then subcommands on grandparents can still be triggered
from the grandchild subcommand, unless subcommand fallthrough is also disabled
on the parent. This is an unusual circumstance but may arise in some very
particular situations.
### Prefix command
This is a special mode that allows "prefix" commands, where the parsing
completely stops when it gets to an unknown option. Further unknown options are
ignored, even if they could match. Git is the traditional example for prefix
commands; if you run git with an unknown subcommand, like "`git thing`", it then
calls another command called "`git-thing`" with the remaining options intact.
### prefix matching
A modifier is available for subcommand matching,
`->allow_subcommand_prefix_matching()`. if this is enabled unambiguous prefix
portions of a subcommand will match. For Example `upgrade_package` would match
on `upgrade_`, `upg`, `u` as long as no other subcommand would also match. It
also disallows subcommand names that are full prefixes of another subcommand.
### Silent subcommands
Subcommands can be modified by using the `silent` option. This will prevent the
subcommand from showing up in the get_subcommands list. This can be used to make
subcommands into modifiers. For example, a help subcommand might look like
```c++
auto sub1 = app.add_subcommand("help")->silent();
sub1->parse_complete_callback([]() { throw CLI::CallForHelp(); });
```
This would allow calling help such as:
```bash
./app help
./app help sub1
```
### Positional Validation
Some arguments supplied on the command line may be legitimately applied to more
than 1 positional argument. In this context calling `validate_positionals()` on
the application or subcommand will check any validators before applying the
command line argument to the positional option. It is not an error to fail
validation in this context, positional arguments not matching any validators
will be treated as extra arguments (retrievable via `app.remaining()`) which may
generate an error depending on settings.
### Optional Argument Validation
Similar to positional validation, there are occasional contexts in which case it
might be ambiguous whether an argument should be applied to an option or a
positional option.
```c++
std::vector<std::string> vec;
std::vector<int> ivec;
app.add_option("pos", vec);
app.add_option("--args", ivec)->check(CLI::Number);
app.validate_optional_arguments();
```
In this case a sequence of integers is expected for the argument and remaining
strings go to the positional string vector. Without the
`validate_optional_arguments()` active it would be impossible get any later
arguments into the positional if the `--args` option is used. The validator in
this context is used to make sure the optional arguments match with what the
argument is expecting and if not the `-args` option is closed, and remaining
arguments fall into the positional.
+40
View File
@@ -0,0 +1,40 @@
# Using CLI11 in a Toolkit {#book-toolkits}
CLI11 was designed to be integrate into a toolkit, providing a native experience
for users. This was used in GooFit to provide `GooFit::Application`, an class
designed to make ROOT users feel at home.
## Custom namespace
If you want to provide CLI11 in a custom namespace, you'll want to at least put
`using CLI::App` in your namespace. You can also include Option, some errors,
and validators. You can also put `using namespace CLI` inside your namespace to
import everything.
You may also want to make your own copy of the `CLI11_PARSE` macro. Something
like:
```cpp
#define MYPACKAGE_PARSE(app, argc, argv) \
try { \
app.parse(argc, argv); \
} catch(const CLI::ParseError &e) { \
return app.exit(e); \
}
```
## Subclassing App
If you subclass `App`, you'll just need to do a few things. You'll need a
constructor; calling the base `App` constructor is a good idea, but not
necessary (it just sets a description and adds a help flag).
You can call anything you would like to configure in the constructor, like
`option_defaults()->take_last()` or `fallthrough()`, and it will be set on all
user instances. You can add flags and options, as well.
## Virtual functions provided
You are given a few virtual functions that you can change (only on the main
App). `pre_callback` runs right before the callbacks run, letting you print out
custom messages at the top of your app.
+81
View File
@@ -0,0 +1,81 @@
# Unicode support {#book-unicode}
CLI11 follows the [UTF-8 Everywhere](http://utf8everywhere.org/) manifesto:
strings are UTF-8 everywhere inside the library, and wide strings are converted
at the edges.
Three things follow from this:
- CLI11 can parse the wide form of the command line on Windows and convert it to
UTF-8 internally.
- An option value can be a `std::wstring`. CLI11 converts it to the correct wide
encoding for your system, UTF-16 on Windows and UTF-32 on most others.
- Rather than store wide strings, it is better to keep `std::string` and convert
only when you must, such as when you call a Windows API.
## Getting correct arguments on Windows
On Windows the `argv` that reaches `main` may already have lost information,
because the arguments were converted to the local code page. Parsing that `argv`
cannot give you the right string back.
The recommended fix works on every platform:
```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);
}
```
On Linux and macOS `ensure_utf8` returns `argv` unchanged. On Windows it
discards `argv` and rebuilds it from the win32 API. Call it before you read or
change `argv`, since the values are reconstructed and any earlier change is
lost.
Two alternatives exist. The first is the Windows-only `wmain` function, which
gets `wchar_t *argv[]`; CLI11 accepts wide arguments directly:
```cpp
int wmain(int argc, wchar_t *argv[]) {
CLI::App app;
// ...
CLI11_PARSE(app, argc, argv);
}
```
The second is to get the arguments yourself with a Windows API such as
`CommandLineToArgvW` and pass them to CLI11. That is what `ensure_utf8` does
internally.
## Converting between narrow 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
}
```
## Unicode paths
A `std::filesystem::path` on Windows must be built from a wide string, or the
name is mangled. `CLI::to_path` does the correct conversion on every platform:
```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));
```
`to_path` needs `<filesystem>` support (C++17, `CLI11_HAS_FILESYSTEM`).
+334
View File
@@ -0,0 +1,334 @@
# Validators {#book-validators}
There are two forms of validators:
- `transform` validators: mutating
- `check` validators: non-mutating (recommended unless the parsed string must be
mutated)
A transform validator comes in one form, a function with the signature
`std::string(std::string&)`. The function will take a string and return an error
message, or an empty string if input is valid. Alternatively, the function may
throw a `CLI::ValidationError` with the appropriate reason as a message; either
returning a non-empty string or throwing signals a failure, so throwing is not
required.
An example of a mutating validator:
```cpp
auto transform_validator = CLI::Validator(
[](std::string &input) {
if (input == "error") {
return "error is not a valid value";
} else if (input == "unexpected") {
throw CLI::ValidationError{"Unexpected error"};
}
input = "new string";
return "";
}, "VALIDATOR DESCRIPTION", "Validator name");
cli_global.add_option("option")->transform(transform_validator);
```
However, `check` validators come in two forms; either a simple function with the
const version of the above signature, `std::string(const std::string &)`, or a
subclass of `struct CLI::Validator`. This structure has two members that a user
should set; one (`func_`) is the function to add to the Option (exactly matching
the above function signature, since it will become that function), and the other
is `name_`, and is the type name to set on the Option (unless empty, in which
case the typename will be left unchanged).
Validators can be combined with `&` and `|`, and inverted with `!`. They have an
`operator()` so that you can call them as if they were a function. In CLI11,
const static versions of the validators are provided so that the user does not
have to call a constructor also.
```cpp
->check(CLI::Range(0, 10) | CLI::Range(20, 30)); // 0-10 or 20-30
->check(!CLI::PositiveNumber); // zero or negative
```
An example of a custom validator:
```cpp
struct LowerCaseValidator : public CLI::Validator {
LowerCaseValidator() {
name_ = "LOWER";
func_ = [](const std::string &str) {
if(CLI::detail::to_lower(str) != str)
return std::string("String is not lower case");
else
return std::string();
};
}
};
const static LowerCaseValidator Lowercase;
```
If you were not interested in the extra features of Validator, you could simply
pass the lambda function above to the `->check()` method of `Option`.
The built-in validators for CLI11 are:
| Validator | Description |
| ------------------- | ---------------------------------------------------------------------- |
| `ExistingFile` | Check for existing file (returns error message if check fails) |
| `ExistingDirectory` | Check for an existing directory (returns error message if check fails) |
| `ExistingPath` | Check for an existing path |
| `NonexistentPath` | Check for an non-existing path |
| `Range(min=0, max)` | Produce a range (factory). Min and max are inclusive. |
| `NonNegativeNumber` | Range(0,max<double>) |
| `PositiveNumber` | Range(denorm_min<double>,max<double>), i.e. any positive number |
A few built-in transformers are also available
| Transformer | Description |
| ------------------- | ---------------------------------------------------------- |
| `EscapedString` | modify a string using defined escape characters |
| `FileOnDefaultPath` | Modify a path if the file is a particular default location |
And, the protected members that you can set when you make your own are:
| Type | Member | Description |
| ------------------------------------------- | -------------------- | ---------------------------------------------------------------------- |
| `std::function<std::string(std::string &)>` | `func_` | Core validation function - modifies input and returns "" if successful |
| `std::function<std::string()>` | `desc_function_` | Optional description function (returns an empty string if not set) |
| `std::string` | `name_` | The name for search purposes |
| `int` (`-1`) | `application_index_` | The element this validator applies to (-1 for all) |
| `bool` (`true`) | `active_` | This can be disabled |
| `bool` (`false`) | `non_modifying_` | Specify that this is a Validator instead of a Transformer |
## Extra Validators
Until CLI11 v3.0 these validators will be available by default. They can be
disabled at compilation time by defining CLI11_DISABLE_EXTRA_VALIDATORS to 1.
After version 3.0 they can be enabled by defining CLI11_ENABLE_EXTRA_VALIDATORS
to 1. Some of the Validators are template heavy so if they are not needed and
compilation time is a concern they can be disabled.
| Validator | Description |
| -------------------- | ------------------------------------------------------------------ |
| `ValidIPV4` | check for valid IPV4 address XX.XX.XX.XX |
| `TypeValidator<T>` | template for checking that a value can convert to a specific type |
| `Number` | Check that a value can convert to a number |
| `IsMember` | Check that a value is one of a set of values |
| `CheckedTransformer` | Values must be one of the transformed set or the result |
| `AsNumberWithUnit` | checks for numbers with a unit as part of a specified set of units |
| `AsSizeValue` | As Number with Unit with support for SI prefixes |
| Transformer | Description |
| --------------------------------- | ---------------------------------------------------------------------- |
| `Bound(min, max)` or `Bound(max)` | Force a range (factory). Min and max are inclusive; min defaults to 0. |
| `Transformer` | Modify values in a set to the matching pair value |
## New Extra Validators
Some additional validators can be enabled by using CLI11_ENABLE_EXTRA_VALIDATORS
to 1. These validators are disabled by default. They also require `<filesystem>`
support (C++17, `CLI11_HAS_FILESYSTEM`).
| Validator | Description |
| ------------------- | ----------------------------------------------------------- |
| `ReadPermissions` | Ensure a file or directory has permissions to read the file |
| `WritePermissions` | Ensure a file or directory has write permissions |
| `ExecPermissions` | Ensure a file has exec permissions |
| `NonEmptyFile` | Ensure that a file exists and is not empty |
| `FileSizeValidator` | specify that the size must be between min and max sizes |
## Sets and transforms
`IsMember` restricts a value to a set. Pass any iterable container with a
`::value_type`, or a copyable pointer to one, or an initializer list:
```cpp
->check(CLI::IsMember({"choice1", "choice2"}));
->check(CLI::IsMember(std::set<int>({2, 3, 4})));
```
After the set you can pass "filter" functions of the form `T(T)`, which are
applied before the comparison. `CLI::ignore_case`, `CLI::ignore_underscore`, and
`CLI::ignore_space` are provided:
```cpp
->check(CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore));
```
`Transformer` and `CheckedTransformer` map one value to another. They take
containers of pairs, so a map is the usual choice:
```cpp
std::map<std::string, int> levels{{"one", 1}, {"two", 2}};
->transform(CLI::CheckedTransformer(levels));
```
`Transformer` passes values that are not in the map through unchanged.
`CheckedTransformer` requires the value to match either a key or one of the
expected outputs, and raises a `ValidationError` if it does not. A `Transformer`
used with `check` does nothing, since `check` may not modify the value.
If you pass a shared pointer to the container instead of the container itself,
you can change the contents later, and the help text and the check both follow
the current contents:
```cpp
auto p = std::make_shared<std::vector<std::string>>(
std::initializer_list<std::string>{"one", "two"});
->check(CLI::IsMember(p));
```
`TransformPairs<T>` is an alias for `std::vector<std::pair<std::string, T>>` for
the same use with the transformers. If the container has a `find` function, like
`std::map`, that function does the search; otherwise the search is linear. With
filters present, the fast search runs first and a filtered linear search is the
fallback.
## Validator operations
A Validator is a copyable object with settings you can change. Every one of
these functions returns a reference to the Validator, so they chain:
| Operation | Effect |
| ------------------------- | -------------------------------------------------------------------------------------------- |
| `.description(text)` | Replace the description shown in the help. |
| `.name(text)` | Name the Validator, so it can be found later with `get_validator(name)`. |
| `.active(bool)` | Turn the Validator on or off. |
| `.application_index(int)` | Apply only to one element of the result. Zero based; a negative index applies to all values. |
| `.operation(func)` | Replace the validation function. |
The application index is what makes validation of compound types work. For a
`std::pair<int, std::string>` where the first element must be positive and the
second must be a file:
```cpp
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
```
A named Validator can be found and changed after the option is built, which is
how you turn a check on or off at runtime:
```cpp
opt->check(CLI::Range(10, 20).description("sensible values").active(false).name("range"));
// later
opt->get_validator("range")->active();
```
`get_validator(name)` throws `CLI::OptionNotFound` if there is no match. With no
name, or an empty name, it returns the first unnamed Validator, or the only one
if there is just one. `get_validator(index)` takes the position in the applied
order instead, and returns `nullptr` for an invalid index.
For reading the current state there are `get_description()`, `get_name()`,
`get_active()`, `get_application_index()`, and `get_modifying()`, which is true
when the Validator may change the value. Modification is controlled by
`non_modifying()`, but it is better to let `check` and `transform` set it.
## Custom Validators
CLI11 also supports the use of custom validators, this includes using the
Validator class constructor with a custom function calls or subclassing
Validator to define a new class.
### Custom Validator operation
The simplest way to make a new Validator is to mimic how many of the existing
Validators are created. Take for example the `IPV4Validator`
```cpp
class IPV4Validator : public Validator {
public:
IPV4Validator();
};
CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
func_ = [](std::string &ip_addr) {
auto cdot = std::count(ip_addr.begin(), ip_addr.end(), '.');
if(cdot != 3u) {
return std::string("Invalid IPV4 address: must have 3 separators");
}
auto result = CLI::detail::split(ip_addr, '.');
if(result.size() != 4) {
return std::string("Invalid IPV4 address: must have four parts (") + ip_addr + ')';
}
int num = 0;
for(const auto &var : result) {
using CLI::detail::lexical_cast;
bool retval = lexical_cast(var, num);
if(!retval) {
return std::string("Failed parsing number (") + var + ')';
}
if(num < 0 || num > 255) {
return std::string("Each IP number must be between 0 and 255 ") + var;
}
}
return std::string{};
};
}
```
The `IPV4Validator` class inherits from `Validator` and creates a new
constructor. In that constructor it defines the lambda function that does the
checking. Then IPV4 can be used like any other Validator. One specific item of
note is that the class does not define any new member variables, so the class if
copyable to a Validator, only the constructor is different.
If additional members are needed, then the `check` and `transform` overloads
that use shared pointers need to be used. The other overloads pass by value so
polymorphism doesn't work. The custom_validator example shows a case like this.
```cpp
template <typename T> class DeltaRange : public CLI::Validator {
public:
T center_point;
T delta;
DeltaRange(const T &center, const T &range)
: CLI::Validator(
[this](const std::string &value) -> std::string {
T newValue;
auto result = CLI::detail::lexical_cast(value, newValue);
if(!(result && this->check(newValue))) {
return std::string("value not within range");
}
return std::string{};
},
"RANGE"),
center_point(center), delta(range) {}
CLI11_NODISCARD bool check(const T &test) const { return (test >= (center_point - delta)) && (test <= (center_point + delta)); }
CLI11_NODISCARD T center() const { return center_point; }
CLI11_NODISCARD T range() const { return delta; }
void center(const T &value) { center_point = value; }
void range(const T &value) { delta = value; }
};
int main(int argc, char **argv) {
/* this application creates custom validator which is a range center+/- range The center and range can be defined by
* other command line options and are updated dynamically
*/
CLI::App app("custom range validator");
std::string value;
auto dr = std::make_shared<DeltaRange<int>>(7, 3);
app.add_option("--number", value, "enter value in the related range")->check(dr)->required();
app.add_option_function<int>("--center", [&dr](int new_center) { dr->center(new_center); })->trigger_on_parse();
app.add_option_function<int>("--range", [&dr](int new_range) { dr->range(new_range); })->trigger_on_parse();
CLI11_PARSE(app, argc, argv);
std::cout << "number " << value << " in range = " << dr->center() << " +/- " << dr->range() << '\n';
return 0;
}
```
The Validator defines some new operations, and in the use case the Validator is
constructed using shared_ptrs. This allows polymorphism to work and the
Validator instance to be shared across multiple options, and as in this example
adapted during the parsing and checking.
There are a few limitation in this, single instances should not be used with
both transform and check. Check modifies some flags in the Validator to prevent
value modification, so that would prevent its use as a transform. Which could be
user modified later but that would potentially allow the check to modify the
value unintentionally.
+38
View File
@@ -0,0 +1,38 @@
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
# under NSF AWARD 1414736 and by the respective contributors.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.14...4.0)
project(CLI11_Examples LANGUAGES CXX)
# Using CMake ability to set imported interface targets
add_library(CLI11::CLI11 IMPORTED INTERFACE)
target_include_directories(CLI11::CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../../include")
target_compile_features(CLI11::CLI11 INTERFACE cxx_std_11)
# Add CTest
enable_testing()
# Quick function to add the base executable
function(add_cli_exe NAME)
add_executable(${NAME} ${NAME}.cpp)
target_link_libraries(${NAME} CLI11::CLI11)
endfunction()
add_cli_exe(simplest)
add_test(NAME simplest COMMAND simplest)
add_cli_exe(intro)
add_test(NAME intro COMMAND intro)
add_test(NAME intro_p COMMAND intro -p 5)
add_cli_exe(flags)
add_test(NAME flags COMMAND flags)
add_test(NAME flags_bip COMMAND flags -b -i -p)
add_cli_exe(geet)
add_test(NAME geet_add COMMAND geet add)
add_test(NAME geet_commit COMMAND geet commit -m "Test")
+36
View File
@@ -0,0 +1,36 @@
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char **argv) {
using std::cout;
using std::endl;
CLI::App app{"Flag example program"};
/// [define]
bool flag_bool;
app.add_flag("--bool,-b", flag_bool, "This is a bool flag");
int flag_int;
app.add_flag("-i,--int", flag_int, "This is an int flag");
CLI::Option *flag_plain = app.add_flag("--plain,-p", "This is a plain flag");
/// [define]
/// [parser]
try {
app.parse(argc, argv);
} catch(const CLI::ParseError &e) {
return app.exit(e);
}
/// [parser]
/// [usage]
cout << "The flags program" << endl;
if(flag_bool)
cout << "Bool flag passed" << endl;
if(flag_int > 0)
cout << "Flag int: " << flag_int << endl;
if(*flag_plain)
cout << "Flag plain: " << flag_plain->count() << endl;
/// [usage]
}
+50
View File
@@ -0,0 +1,50 @@
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char **argv) {
/// [Intro]
CLI::App app{"Geet, a command line git lookalike that does nothing"};
app.require_subcommand(1);
/// [Intro]
/// [Add]
auto add = app.add_subcommand("add", "Add file(s)");
bool add_update;
add->add_flag("-u,--update", add_update, "Add updated files only");
std::vector<std::string> add_files;
add->add_option("files", add_files, "Files to add");
add->callback([&]() {
std::cout << "Adding:";
if(add_files.empty()) {
if(add_update)
std::cout << " all updated files";
else
std::cout << " all files";
} else {
for(auto file : add_files)
std::cout << " " << file;
}
});
/// [Add]
/// [Commit]
auto commit = app.add_subcommand("commit", "Commit files");
std::string commit_message;
commit->add_option("-m,--message", commit_message, "A message")->required();
commit->callback([&]() { std::cout << "Commit message: " << commit_message; });
/// [Commit]
/// [Parse]
CLI11_PARSE(app, argc, argv);
std::cout << "\nThanks for using geet!\n" << std::endl;
return 0;
/// [Parse]
}
+15
View File
@@ -0,0 +1,15 @@
#include "CLI/CLI.hpp"
#include <iostream>
int main(int argc, char **argv) {
CLI::App app{"App description"};
// Define options
int p = 0;
app.add_option("-p", p, "Parameter");
CLI11_PARSE(app, argc, argv);
std::cout << "Parameter value: " << p << std::endl;
return 0;
}
+11
View File
@@ -0,0 +1,11 @@
#include "CLI/CLI.hpp"
int main(int argc, char **argv) {
CLI::App app;
// Add new options/flags here
CLI11_PARSE(app, argc, argv);
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 982 KiB