CPP-AP 2.7.0
Command-line argument parser for C++20
|
Important:
This tutorial covers the most common use cases and features of the library. For more in-depth information and advanced usage, please refer to the full documentation. Instructions for building the documentation are available in the Dev Notes page.
For CMake projects you can simply fetch the library in your CMakeLists.txt
file:
To use the CPP-AP
in a Bazel project add the following in the MODULE.bazel
(or WORKSPACE.bazel
) file:
Important: CPP-AP versions older than 2.5.0 DO NOT support building with Bazel.
And then add the "@cpp-ap//:cpp-ap"
dependency for the target you want to use CPP-AP
for by adding it to the deps
list. For instance:
If you do not use CMake you can dowload the desired library release, extract it in a desired directory and simply add <cpp-ap-dir>/include
to the include directories of your project.
To use the argument parser in your code you need to use the ap::argument_parser
class.
The parameters you can specify for a parser's instance are:
std::cout << parser
).false
by default; if set to true
the parser's configuration output will include more detailed info about arguments' parameters in addition to their names and help messages. Tip:
You can specify the program version using a string (like in the example above) or using the
ap::version
structure:parser.program_version({0u, 0u, 0u})parser.program_version({ .major = 1u, .minor = 1u, .patch = 1u });ap::version ver{2u, 2u, 2u};parser.program_version(ver);NOTE: The
ap::version
struct
- contains the three members -
major
,minor
,patch
- all of which are of typestd::uint32_t
,- defines a
std::string str() const
method which returns av{major}.{minor}.{path}
version string,- defines the
std::ostream& operator<<
for stream insertion.
The parser supports both positional and optional arguments. Both argument types are identified by their names represented as strings. Arguments can be defined with only a primary name or with a primary and a secondary (short) name.
Note:
The basic rules of parsing positional and optional arguments are described in the Parsing arguments section.
To add an argument to the parameter's configurations use the following syntax:
or
Important:
The library supports any argument value types which meet the following requirements:
The type is constructible from
const std::string&
or the stream extraction operator -std::istream& operator>>
is defined for the type.IMPORTANT: The argument parser will always use direct initialization from
std::string
and will use the extraction operator only if an argument's value type cannot be initialized fromstd::string
.- The type satisfies the
std::semiregular
concept - is default initializable and copyable.
Note:
The default value type of any argument is
std::string
.
You can also add boolean flags:
Boolean flags store true
by default but you can specify whether the flag should store true
or false
when used:
Note:
While passing a primary name is required for creating positional arguments, optional arguments (and flags) can be initialized using only a secondary name as follows:
parser.add_optional_argument("a", ap::n_secondary);parser.add_flag("f", ap::n_secondary);argument::optional< T > & add_optional_argument(const std::string_view name, const detail::argument_name_discriminator name_discr=n_primary)Adds a positional argument to the parser's configuration.Definition argument_parser.hpp:216
Parameters which can be specified for both positional and optional arguments include:
help
- The argument's description which will be printed when printing the parser class instance.hidden
- If this option is set for an argument, then it will not be included in the program description.By default all arguments are visible, but this can be modified using the hidden(bool)
setter as follows:
required
- If this option is set for an argument and it's value is not passed in the command-line, an exception will be thrown. Note:
- By default positional arguments are set to be required, while optional arguments have this option disabled by default.
- The default value of the value parameter of the
required(bool)
function istrue
for both positional and optional arguments.
Warning:
- If a positional argument is defined as non-required, then no required positional argument can be defined after (only other non-required positional arguments and optional arguments will be allowed).
- For both positional and optional arguments:
- enabling the
required
option disables thebypass_required
option- disabling the
required
option has no effect on thebypass_required
option.
bypass_required
- If this option is set for an argument, the required
option for other arguments will be discarded if the bypassing argument is used in the command-line. Note:
- Both positional and optional arguments have the
bypass_required
option disabled.- The default value of the value parameter of the
bypass_required(bool)
function istrue
for both positional and optional arguments.
Warning:
For both positional and optional arguments:
- enabling the
bypass_required
option disables therequired
option- disabling the
bypass_required
option has no effect on therequired
option.
default_value
- The default value for an argument which will be used if no values for this argument are parsed Warning:
For both positional and optional arguments, setting the
default_value
parameter disables therequired
option.
Tip:
The setter of the
default_value
parameter accepts any type that is convertible to the argument's value type.
choices
- A list of valid argument values.The choices
parameter takes as an argument an instance of std::initializer_list
or any std::ranges::range
type such that its value type is convertible to the argument's value_type
.
Important:
The
choices
function can be used only if the argument'svalue_type
is equality comparable (defines the==
operator).
Actions are represented as functions, which take the argument's value as an argument. The available action types are:
observe
actions | void(const value_type&)
- applied to the parsed value. No value is returned - this action type is used to perform some logic on the parsed value without modifying it.
transform
actions | value_type(const value_type&)
- applied to the parsed value. The returned value will be used to initialize the argument's value.
modify
actions | void(value_type&)
- applied to the initialized value of an argument.
Tip:
A single argument can have multiple value actions. Instead of writing complex logic in one action, consider composing several simple, focused actions for better readability and reusability.
Apart from the common parameters listed above, for optional arguments you can also specify the following parameters:
nargs
- Sets the allowed number of values to be parsed for an argument. This can be set as a:Specific number:
Fully bound range:
Partially bound range:
Unbound range:
Important:
The default
nargs
parameter value isap::nargs::any()
.
implicit_value
- A value which will be set for an argument if only it's flag is parsed from the command-line but no values follow. Tip:
- The
implicit_value
parameter is extremely useful when combined with default value (e.g. in case of boolean flags - see Adding Arguments).- The setter of the
implicit_value
parameter accepts any type that is convertible to the argument's value type.
Here the print_debug_info
function will be called right after parsing the --debug-info
flag and the program will exit, even if there are more arguments after this flag.
print_config
| on-flag
Prints the configuration of the parser to the output stream and optionally exits with the given code.
check_file_exists
| observe (value type: std::string
)
Throws if the provided file path does not exist.
gt
| observe (value type: arithmetic)
Validates that the value is strictly greater than lower_bound
.
geq
| observe (value type: arithmetic)
Validates that the value is greater than or equal to lower_bound
.
lt
| observe (value type: arithmetic)
Validates that the value is strictly less than upper_bound
.
leq
| observe (value type: arithmetic)
Validates that the value is less than or equal to upper_bound
.
within
| observe (value type: arithmetic)
Checks if the value is within the given interval. Bound inclusivity is customizable using template parameters.
The CPP-AP
library defines several default arguments, which can be added to the parser's configuration as follows.
Note:
These functions work with
std::initializer_list
and all otherstd::ranges::range
types with the correct value type -ap::argument::default_{positional/optional}
The available default arguments are:
default_positional::input
:
default_positional::output
:
default_optional::help
:
default_optional::input
and default_optional::multi_input
:
default_optional::output
and default_optional::multi_output
:
To parse the command-line arguments use the void argument_parser::parse_args(const AR& argv)
method, where AR
must be a type that satisfies std::ranges::range
and its value type is convertible to std::string
.
The argument_parser
class also defines the void parse_args(int argc, char* argv[])
overload, which works directly with the argc
and argv
arguments of the main
function.
Important:
The
parse_args(argc, argv)
method ignores the first argument (the program name) and is equivalent to calling:parse_args(std::span(argv + 1, argc - 1));
Warning:
By default the
argument_parser
class treats all* command-line arguments beggining with a--
or-
prefix as optional argument flags and if the flag's value does not match any of the specified arguments, then such flag is considered unknown and an exception will be thrown.[all*] If a command-line argument begins with a flag prefix, but contains whitespaces (e.g.
"--flag value"
), then it is treated as a value and not a flag.This behavior can be altered so that the unknown argument flags will be treated as values, not flags.
Example:
To do this add the following in you
CMakeLists.txt
file:target_compile_definitions(cpp-ap PRIVATE AP_UNKNOWN_FLAGS_AS_VALUES)or simply add:
#define AP_UNKNOWN_FLAGS_AS_VALUESbefore the
#include <ap/argument_parser.hpp>
statement.
Tip:
The
parse_args
function may throw anap::argument_parser_exception
(specifically theap::parsing_failure
derived exception) if the provided command-line arguments do not match the expected configuration. To simplify error handling, theargument_parser
class providestry_parse_args
methods, which will automatically catch these exceptions, print the error message, and exit with a failure status.Internally, This is equivalent to:
try {parser.parse_args(...);}std::cerr << "[ERROR] : " << err.what() << std::endl << parser << std::endl;std::exit(EXIT_FAILURE);}Base type for the argument parser functionality errors/exceptions.Definition exceptions.hpp:18
Positional arguments are parsed first, in the order they were defined in and without a flag.
In the example above the first command-line argument must be the value for the positional
argument:
Important:
For each positional argument there must be exactly one value.
Optional arguments are parsed only with a flag. The values passed after an argument flag will be treated as the values of the last optional argument that preceeds them. If no argument flag preceeds a value argument, then it will be treated as an unknown value.
You can use the flag for each command-line value:
Not using a flag will result in an error:
Warning:
If an optional argument has the
nargs
parameter set with an upper bound, then the values that succeed this argument's flag will be assigned to this argument only until the specified upper bound is reached. Further values will be treated as unknown values.Example:
./power 2 -e 1 2 3 4 5[ERROR] : Failed to deduce the argument for values [4, 5]Program: power calculatorCalculates the value of an expression: base ^ exponentPositional arguments:base : the exponentation base valueOptional arguments:--exponent, -e : the exponent value--help, -h : Display the help message
Compound argument flags are secondary argument flags of which every character matches the secondary name of an optional argument.
Example:
Important:
- If there exists an argument whose secondary name matches a possible compound of other arguments, the parser will still treat the flag as a flag of the single matching argument, not as multiple flags.
- The argument parser will try to assign the values following a compound argument flag to the argument represented by the last character of the compound flag.
If you wish to handle only the specified command-line arguments and leave all unkown/unrecognized arguments, you can use the parse_known_args
method.
This method behaves similarly to parse_args()
(see Parsing Arguments), however it does not throw an error if unknown arguments are detected. Instead it returnes all the unknown arguments detected during parsing as a std::vector<std::string>
.
Consider a simple example:
Here the parser throws exceptions for arguments it doesn't recognize. Now consider the same example with parse_known_args
:
Now all the values, that caused an exception for the parse_args
example, are collected and returned as the result of argument parsing.
Important:
If a parser encounters an unrecognized argument flag during known args parsing, then the flag will be collected and the currently processed optional argument will be reset. That means that any value following an unrecognized flag will be treated as an unknown argument as well. Let's consider an example:
.nargs(ap::nargs::any()) // don't restrict the number of arguments.help("A recognized optional argument");<< "unkown = " << join(unknown_args) << std::endl;/* Example execution:./program value0 --recognized value1 value2 value3 --unrecognized value --recognized value4recognized = value1, value2, value3, value4unkown = value0, --unrecognized, valueHere
value
is treated as an unknown argument even though therecognized
optional argument still accepts values and only after a different flag is encountered the parser stops collecting the values to the unknown arguments list.NOTE: If the
AP_UNKNOWN_FLAGS_AS_VALUES
is set, the unrecognized argument flags will be treated as values during parsing and therefore they may not be collected as unknown arguments, depending on the argument's configuration and the command-line argument list.
Tip:
Similarly to the
parse_args
method,parse_known_args
has atry
equivalent -try_parse_known_args
- which will automatically catch these exceptions, print the error message, and exit with a failure status.
You can retrieve the argument's value with:
This will return the value parsed for the given argument.
For optional arguments this will return the argument's predefined value if no value has been parsed. Additionaly, if more than one value has been parsed for an optional argument, this function will return the first parsed value.
value_type{std::forward<U>(fallback_value)}
(where U
is the deducted type of fallback_value
), if:
Additionally for optional arguments, you can use:
which returns a vector
containing all values parsed for the given argument.
The library usage examples / demo projects can be found in the cpp-ap-demo repository.