CLIUtils/CLI11(opens in a new tab)
Language: C++
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
- stars
- 4,512
- watchers
- 44
- forks
- 489
- open issues
- 86
- Last updated
- 10/23/2026
Language: C++
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
What's new • Documentation • API Reference
CLI11 is a command line parser for C++11 and beyond that provides a rich feature set with a simple and intuitive interface.
Features that were added in the last released minor version are marked with "🆕". Features only available in main are marked with "🚧".
CLI11 provides all the features you expect in a powerful command line parser,
with a beautiful, minimal syntax and no dependencies beyond C++11. It is
header-only, and comes in a single file form for easy inclusion in projects. It
is easy to use for small projects, but powerful enough for complex command line
projects, and can be customized for frameworks. It is tested on Azure and
GitHub Actions, and was originally used by the GooFit GPU
fitting framework. It was inspired by plumbum.cli for
Python. CLI11 has a user-friendly introduction in this README, a more in-depth
tutorial book, as well as API documentation generated by
Doxygen. See the changelog or GitHub Releases for details
on current and past releases. Also see the Version 1.0 post, Version 1.3
post, Version 1.6 post, or Version 2.0 post for more information.
You can be notified when new releases are made by subscribing to https://github.com/CLIUtils/CLI11/releases.atom on an RSS reader, like Feedly, or use the releases mode of the GitHub watching tool.
An acceptable CLI parser library should be all of the following:
TOML, INI, or custom format), and
produce it as well.
| Library | My biased opinion |
|---|---|
| Boost Program Options | A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO. See this comparison. |
| The Lean Mean C++ Option Parser | One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well. |
| TCLAP | The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produces wrapped values. |
| Cxxopts | C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar. |
| DocOpt | Completely different approach to program options in C++11, you write the docs and the interface is generated. Too fragile and specialized. |
After I wrote this, I also found the following libraries:
| Library | My biased opinion |
|---|---|
| GFlags | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. |
| GetOpt | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). |
| ProgramOptions.hxx | Interesting library, less powerful and no subcommands. Nice callback system. |
| Args | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. |
| Argument Aggregator | I'm a big fan of the fmt library, and the try-catch statement looks familiar. 👍 Doesn't seem to support subcommands. |
| Clara | Simple library built for the excellent Catch testing framework. Unique syntax, limited scope. |
| Argh! | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free. |
| CLI | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active. |
| argparse | C++17 single file argument parser. Design seems similar to CLI11 in some ways. The author has several other interesting projects. |
| lyra | a simple header only parser with composable options. Might work well for simple standardized parsing |
See Awesome C++ for a less-biased list of parsers. You can also find other single file libraries at Single file libs.
None of these libraries fulfill all the above requirements, or really even come close. As you probably have already guessed, CLI11 does. So, this library was designed to provide a great syntax, good compiler compatibility, and minimal installation fuss.
There are some other possible "features" that are intentionally not supported by this library:
argparse supplies for
incomplete arguments. It's better not to guess. Most third party command line
parsers for python actually reimplement command line parsing rather than using
argparse because of this perceived design flaw (recent versions do have an
option to disable it). Recent releases of CLI11 include optional unambiguous
prefix matching for subcommand names, enabled by
.allow_subcommand_prefix_matching(), along with an example that generates
suggested close matches.-option. This is enabled through allow_non_standard_option_names()
modifier on an app.To use, the most common methods are described here, additional methods and details are available at installation:
CLI11.hpp from the most recent
release into your include directory, and you are set. This
is combined from the source files for every release. This includes the entire
command parser library, but does not include separate utilities (like Timer,
AutoTimer). The utilities are completely self-contained and can be copied
separately./opt/CLI11. Then, the C++ include path has to be extended to
point at this folder. With CMake 3.10+, use include_directories(/opt/CLI11)-DCLI11_MODULES=ON (CMake 3.28+
and a module-capable generator), link CLI11::Module, and write
import cli11;. See installation for details.CLI11 is header-only by default, so each translation unit that includes it
compiles the full library. If you build CLI11 with CMake (add_subdirectory or
an installed package), you can set CLI11_PRECOMPILED to compile the
implementation one time into a static library instead:
cmake -S . -B build -DCLI11_PRECOMPILED=ONThis can markedly reduce the compile time of each file that includes CLI11 (in
one simple measurement, approximately 4x faster per file). The CLI11::CLI11
target does not change. See installation for details.
To set up, add options, and run, your main function will look something like this:
int main(int argc, char** argv) { CLI::App app{"App description"}; argv = app.ensure_utf8(argv); std::string filename = "default"; app.add_option("-f,--file", filename, "A help string"); CLI11_PARSE(app, argc, argv); return 0; }
When adding options the names should not conflict with each other, if an option
is added, or a modifier changed that would cause naming conflicts a run time
error will be thrown in the add_option method. This includes default options for
help -h, --help. For more information about ensure_utf8 see the section on
Unicode support below.
try { app.parse(argc, argv); } catch (const CLI::ParseError &e) { return app.exit(e); }
The try/catch block ensures that -h,--help or a parse error will exit with the
correct return code (selected from CLI::ExitCodes). (The return here should be
inside main). You should not assume that the option values have been set
inside the catch block; for example, help flags intentionally short-circuit all
other processing for speed and to ensure required options and the like do not
interfere.
The initialization is just one line, adding options is just two each. The parse
macro is just one line (or 5 for the contents of the macro). After the app runs,
the filename will be set to the correct value if it was passed, otherwise it
will be set to the default. You can check to see if this was passed on the
command line with app.count("--file").
While all options internally are the same type, there are several ways to add an option depending on what you need. The supported values are:
// Add options app.add_option(option_name, help_str="") app.add_option(option_name, variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectors of any other supported type. help_string="") app.add_option_function<type>(option_name, function <void(const type &value)>, // type can be any type supported by add_option help_string="") // char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values. // There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type app.add_option<typename T, typename XC>(option_name, T &output, // output must be assignable or constructible from a value of type XC help_string="") // Add flags app.add_flag(option_name, help_string="") app.add_flag(option_name, variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic, or string-like, or any singular object with a defined conversion from a string like add_option help_string="") app.add_flag_function(option_name, function <void(std::int64_t count)>, help_string="") app.add_flag_callback(option_name,function<void(void)>,help_string="") // Add subcommands App* subcom = app.add_subcommand(name, description); Option_group *app.add_option_group(name,description);
An option name may start with any character except ('-', ' ', '\n', and '!').
For long options, after the first character all characters are allowed except
('=',':','{',' ', '\n'). For the add_flag* functions '{' and '!' have special
meaning which is why they are not allowed. Names are given as a comma separated
string, with the dash or dashes. An option or flag can have as many names as you
want, and afterward, using count, you can use any of the names, with dashes as
needed, to count the options. One of the names is allowed to be given without
proceeding dash(es); if present the option is a positional option, and that name
will be used on the help line for its positional form. The string ++ is also
not allowed as option name due to its use as an array separator and marker on
config files.
The add_option_function<type>(... function will typically require the template
parameter be given unless a std::function object with an exact match is
passed. The type can be any type supported by the add_option function. The
function should throw an error (CLI::ConversionError or CLI::ValidationError
possibly) if the value is not valid.
The two parameter template overload can be used in cases where you want to restrict the input such as
double val; app.add_option<double,unsigned int>("-v",val);
which would first verify the input is convertible to an unsigned int before
assigning it. Or using some variant type
using vtype=std::variant<int, double, std::string>; vtype v1; app.add_option<vtype,std::string>("--vs",v1); app.add_option<vtype,int>("--vi",v1); app.add_option<vtype,double>("--vf",v1);
otherwise the output would default to a string. The add_option can be used
with any integral or floating point types, enumerations, or strings. Or any type
that takes an int, double, or std::string in an assignment operator or
constructor. If an object can take multiple varieties of those, std::string
takes precedence, then double then int. To better control which one is used or
to use another type for the underlying conversions use the two parameter
template to directly specify the conversion type.
Types such as (std or boost) optional<int>, optional<double>, and
optional<string> and any other wrapper types are supported directly. For
purposes of CLI11 wrapper types are those which have a value_type definition.
See CLI11 Advanced Topics/Custom Converters for information on how you can
add your own converters for additional types.
Vector types can also be used in the two parameter template overload
std::vector<double> v1; app.add_option<std::vector<double>,int>("--vs",v1);
would load a vector of doubles but ensure all values can be represented as integers.
Use default_str(...) or default_val(...) to set the default string or value
for the option or flag. Use ->default_function(std::string()) to customize the
default capture function directly. The default value is then captured by calling
->capture_default_str().
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:
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:
app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag")
Using any of those flags on the command line will result in the specified number in the output. Similar things can be done for string values, and enumerations, as long as the default value can be converted to the given type.
On a C++14 compiler, you can pass a callback function directly to .add_flag,
while in C++11 mode you'll need to use .add_flag_function if you want a
callback function. The function will be given the number of times the flag was
passed. You can throw a relevant CLI::ParseError to signal a failure.
"one,-o,--one": Valid as long as not a flag, would create an option that can
be specified positionally, or with -o or --one"this" Can only be passed positionally"-a,-b,-c" No limit to the number of non-positional option namesThe add commands return a pointer to an internally stored Option. This option
can be used directly to check for the count (->count()) after parsing to avoid
a string based lookup.
Before parsing, you can set the following options:
->required(): The program will quit if this option is not present. This is
mandatory in Plumbum, but required options seems to be a more standard term.
For compatibility, ->mandatory() also works.->expected(N): Take N values instead of as many as possible, only for
vector args. If negative, require at least -N; end with -- or another
recognized option or subcommand.->expected(MIN,MAX): Set a range of expected values to accompany an option.
expected(0,1) is the equivalent of making a flag.->type_name(typename): Set the name of an Option's type (type_name_fn
allows a function instead)->type_size(N): Set the intrinsic size of an option value. The parser will
require multiples of this number if negative. Most of the time this is
detected automatically though can be modified for specific use cases.->type_size(MIN,MAX): Set the intrinsic size of an option to a range.->needs(opt): This option requires another option to also be present, opt is
an Option pointer. Options can be removed from the needs with
remove_needs(opt). The option can also be specified with a string containing
the name of the option->excludes(opt): This option cannot be given with opt present, opt is an
Option pointer. Can also be given as a string containing the name of the
option. Options can be removed from the excludes list with
->remove_excludes(opt)->envname(name): Gets the value from the environment if present and not
passed on the command line. The value must also pass any validators to be
used.->group(name): The help group to put the option in. No effect for positional
options. Defaults to "Options". Options given an empty string will not show
up in the help print (hidden).->ignore_case(): Ignore the case on the command line (also works on
subcommands, does not affect arguments).->ignore_underscore(): Ignore any underscores in the options names (also
works on subcommands, does not affect arguments). For example "option_one"
will match with "optionone". This does not apply to short form options since
they only have one character->disable_flag_override(): From the command line long form flag options can
be assigned a value on the command line using the = notation --flag=value.
If this behavior is not desired, the disable_flag_override() disables it and
will generate an exception if it is done on the command line. The = does not
work with short form flag options.->allow_extra_args(true/false): If set to true the option will take an
unlimited number of arguments like a vector, if false it will limit the number
of arguments to the size of the type used in the option. Default value depends
on the nature of the type use, containers default to true, others default to
false. Set it to false to stop a container option from taking the arguments
that follow it, for example to keep them available for a positional.->delimiter(char): Allows specification of a custom delimiter for separating
single arguments into vector arguments, for example specifying
->delimiter(',') on an option would result in --opt=1,2,3 producing 3
elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a
vector value. A delimiter does not restrict the option to one argument; add
->allow_extra_args(false) if only the delimited argument must be taken, so
--opt 1,2,3 pos gives pos to a positional instead of to --opt.->description(str): Set/change the description.->multi_option_policy(CLI::MultiOptionPolicy::Throw): Set the multi-option
policy. Shortcuts available: ->take_last(), ->take_first(),->take_all(),
and ->join(). This will only affect options expecting 1 argument or bool
flags (which do not inherit their default but always start with a specific
policy). ->join(delim) can also be used to join with a specific delimiter.
This equivalent to calling ->delimiter(delim) and ->join(). Valid values
are CLI::MultiOptionPolicy::Throw, CLI::MultiOptionPolicy::TakeLast,
CLI::MultiOptionPolicy::TakeFirst, CLI::MultiOptionPolicy::Join,
CLI::MultiOptionPolicy::TakeAll, CLI::MultiOptionPolicy::Sum, and
CLI::MultiOptionPolicy::Reverse.->check(std::string(const std::string &), validator_name="",validator_description=""):
Define a check function. The function should return a non empty string with
the error message if the check fails->check(Validator): Use a Validator object to do the check see
Validators for a description of available Validators and how to
create new ones.->transform(std::string(std::string &), validator_name="",validator_description=""):
Converts the input string into the output string, in-place in the parsed
options.->transform(Validator): Uses a Validator object to do the transformation see
Validators for a description of available Validators and how to
create new ones.->each(void(const std::string &)>: Run this function on each value received,
as it is received. It should throw a ValidationError if an error is
encountered.->configurable(false): Disable this option from being in a configuration
file.->capture_default_str(): Store the current value attached and display it in
the help string. This should work with almost any type that add_option can
accept.->default_function(std::string()): Advanced: Change the function that
capture_default_str() uses.->always_capture_default(): Always run capture_default_str() when creating
new options. Only useful on an App's option_defaults.->default_str(string): Set the default string directly (NO VALIDATION OR
CALLBACKS). This string will also be used as a default value if no arguments
are passed and the value is requested.->default_val(value): Generate the default string from a value and validate
that the value is also valid. For options that assign directly to a value type
the value in that type is also updated. Value must be convertible to a
string(one of known types or have a stream operator). The callback may be
triggered if the run_callback_for_default is set.->run_callback_for_default(): This will force the option callback to be
executed or the variable set when the default_val is set.->option_text(string): Sets the text between the option name and
description.->force_callback(): Causes the option callback or value set to be triggered
even if the option was not present in parsing.->trigger_on_parse(): If set, causes the callback and all associated
validation checks for the option to be executed when the option value is
parsed vs. at the end of all parsing. This could cause the callback to be
executed multiple times. Also works with positional options.->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.These options return the Option pointer, so you can chain them together, and
even skip storing the pointer entirely. The each function takes any function
that has the signature void(const std::string&); it should throw a
ValidationError when validation fails. The help message will have the name of
the parent option prepended. Since each, check and transform use the same
underlying mechanism, you can chain as many as you want, and they will be
executed in order. Operations added through transform are executed first in
reverse order of addition, and check and each are run following the
transform functions in order of addition. If you just want to see the
unconverted values, use .results() to get the std::vector<std::string> of
results.
On the command line, options can be given as:
-a (flag)-abc (flags can be combined)-f filename (option)-ffilename (no space required)-abcf filename (flags and option can be combined)--long (long flag)--long_flag=true (long flag with equals -- to override default value)--file filename (space)--file=filename (equals)If allow_windows_style_options() is specified in the application or subcommand
options can also be given as:
/a (flag)/f filename (option)/long (long flag)/file filename (space)/file:filename (colon)/long_flag:false (long flag with : to override the default value)
- optionsLong flag options may be given with an =<value> to allow specifying a false
value, or some other value to the flag. See config files
for details on the values supported. NOTE: only the = or : for windows-style
options may be used for this, using a space will result in the argument being
interpreted as a positional argument. This syntax can override the default
values, and can be disabled by using disable_flag_override().
Extra positional arguments will cause the program to exit, so at least one
positional option with a vector is recommended if you want to allow extraneous
arguments. If you set .allow_extras() on the main App, you will not get an
error. You can access the missing options using remaining (if you have
subcommands, app.remaining(true) will get all remaining options, subcommands
included). If the remaining arguments are to processed by another App then the
function remaining_for_passthrough() can be used to get the remaining
arguments in reverse order such that app.parse(vector) works directly and
could even be used inside a subcommand callback.
You can access a vector of pointers to the parsed options in the original order
using parse_order(). If -- is present in the command line that does not end
an unlimited option, then everything after that is positional only.
Validators are structures to check or modify inputs, they can be used to verify
th