- Setting Up CPP-AP
- The Parser Class
- Adding Arguments
- Argument Parameters
- Predefined Parameter Values
- Default Arguments
- Parsing Arguments
- Retrieving Argument Values
- Examples
- Common Utility
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:
cmake_minimum_required(VERSION 3.12)
project(my_project LANGUAGES CXX)
# Include FetchContent module
include(FetchContent)
# Fetch CPP-AP library
FetchContent_Declare(
cpp-ap
GIT_REPOSITORY https://github.com/SpectraL519/cpp-ap.git
GIT_TAG master # here you can specify the desired tag or branch name
)
FetchContent_MakeAvailable(cpp-ap)
# Define the executable for the project
add_executable(my_project main.cpp)
set_target_properties(my_project PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
)
# Link against the cpp-ap library
target_link_libraries(my_project PRIVATE cpp-ap)To use the CPP-AP in a Bazel project add the following in the MODULE.bazel (or WORKSPACE.bazel) file:
git_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "cpp-ap",
remote = "https://github.com/SpectraL519/cpp-ap.git",
tag = "<version-name>" # here you can declare the desired CPP-AP version
)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:
# BUILD.bazel
cc_binary(
name = "my_app",
srcs = ["application.cpp"],
includes = ["include"],
deps = ["@cpp-ap//:cpp-ap"],
cxxopts = ["-std=c++20"],
visibility = ["//visibility:public"],
)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:
- The program's name, version and description - used in the parser's configuration output (
std::cout << parser). - Verbosity mode -
falseby default; if set totruethe parser's configuration output will include more detailed info about arguments' parameters in addition to their names and help messages. - Arguments - specify the values/options accepted by the program.
- The unknown argument flags handling policy.
ap::argument_parser parser;
parser.program_name("Name of the program")
.program_version("alhpa")
.program_description("Description of the program")
.verbose();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() constmethod 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.
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:
parser.add_<positional/optional>_argument<value_type>("argument");or
parser.add_<positional/optional>_argument<value_type>("argument", "a");Note
An argument's name consists of a primary and/or secondary names. The primary name is a longer, more descriptive name, while the secondary name is a shorter/abbreviated name of the argument.
While passing a primary name is required for creating positional arguments, optional arguments 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);Important
An argument's value type must be ap::none_type or it must satisfy 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::stringand will use the extraction operator only if an argument's value type cannot be initialized fromstd::string. -
The type satisfies the
std::semiregularconcept - is default initializable and copyable.
Note
-
The default value type of any argument is
std::string. -
If the argument's value type is
ap::none_type, the argument will not accept any values and therefore no value-related parameters can be set for such argument. This includes:
You can also add boolean flags:
parser.add_flag("enable_some_option", "eso").help("enables option: some option");
/* equivalent to:
parser.add_optional_argument<bool>("enable_some_option", "eso")
.default_values(false)
.implicit_values(true)
.nargs(0)
.help("enables option: some option");
*/Boolean flags store true by default but you can specify whether the flag should store true or false when used:
parser.add_flag<false>("disable_another_option", "dao").help("disables option: another option");
/* equivalent to:
parser.add_optional_argument<bool>("disable_another_option", "dao")
.default_values(true)
.implicit_values(false)
.nargs(0)
.help("disables option: another option");
*/Parameters which can be specified for both positional and optional arguments include:
parser.add_positional_argument<std::size_t>("number", "n")
.help("a positive integer value");2. 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:
parser.program_name("hidden-test")
.program_description("A simple program")
.default_arguments(ap::default_argument::o_help);
parser.add_optional_argument("hidden")
.hidden()
.help("A simple hidden argument");
parser.add_optional_argument("visible")
.help("A simple visible argument");
parser.try_parse_args(argc, argv);
/*
> ./hidden-test --help
Program: hidden-test
A simple program
Optional arguments:
--help, -h : Display the help message
--visible : A simple visible argument3. 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.
Important
- 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 istruefor 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
requiredoption disables thebypass_requiredoption - disabling the
requiredoption has no effect on thebypass_requiredoption.
- enabling the
// example: positional arguments
parser.add_positional_argument("input");
parser.add_positional_argument("output").required(false);
parser.parse_args(argc, argv);
// input is guaranteed to have a value if parsing was successfull
const auto data = read_data(parser.value("input"));
if (parser.has_value("output")) {
std::ofstream os(parser.value("output"));
os << data << std::endl;
}
else {
std::cout << data << std::endl;
}
/*
Command Result
-----------------------------------------------------------------------------------------
./program Parsing error (no value for the input argument)
./program input.txt Parsing success; Printing data to stdout
./program input.txt output.txt Parsing success; Printing data to the `output.txt` file// example: optional arguments
parser.add_optional_argument("input", "i").required();
parser.add_optional_argument("output", "o");
parser.parse_args(argc, argv);
// `input` is guaranteed to have a value if parsing was successfull
const auto data = read_data(parser.value("input"));
if (parser.has_value("output")) {
std::ofstream os(parser.value("output"));
os << data << std::endl;
}
else {
std::cout << data << std::endl;
}
/*
Command Result
-----------------------------------------------------------------------------------------------
./program Parsing error (no value for the input argument)
./program --input input.txt Parsing success; Printing data to stdout
./program -i input.txt -o output.txt Parsing success; Printing data to the `output.txt` file
*/4. 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_requiredoption disabled. - The default value of the value parameter of the
bypass_required(bool)function istruefor both positional and optional arguments.
Warning
For both positional and optional arguments:
- enabling the
bypass_requiredoption disables therequiredoption - disabling the
bypass_requiredoption has no effect on therequiredoption.
// example: optional arguments
parser.add_positional_argument("input");
parser.add_optional_argument("output", "o").required();
parser.add_optional_argument("version", "v").bypass_required();
parser.parse_args(argc, argv);
if (parser.count("version")) {
std::cout << PROJECT_VERSION << std::endl;
std::exit(EXIT_SUCCESS);
}
// may result in an `ap::argument_parser_exception`:
// `input` is not guaranteed to have a value at this point
const auto data = read_data(parser.value("input"));
// may result in an `ap::argument_parser_exception`:
// `output` is not guaranteed to have a value at this point
std::ofstream os(parser.value("output"));
os << data << std::endl;-
Specific number:
parser.add_optional_argument("input", "i").nargs(1);
-
Fully bound range:
parser.add_optional_argument("input", "i").nargs(1, 3);
-
Partially bound range:
parser.add_optional_argument("input", "i").nargs(ap::nargs::at_least(1)); // n >= 1 parser.add_optional_argument("input", "i").nargs(ap::nargs::more_than(1)); // n > 1 parser.add_optional_argument("input", "i").nargs(ap::nargs::less_than(5)); // n < 5 parser.add_optional_argument("input", "i").nargs(ap::nargs::up_to(5)); // n <= 5
-
Unbound range:
parser.add_optional_argument("input", "i").nargs(ap::nargs::any());
Important
The default nargs parameter value is:
-
ap::nargs::range(1ull)for positional arguments -
ap::nargs::any()for optional arguments
parser.add_optional_argument<char>("method", "m").choices('a', 'b', 'c');
// equivalent to: parser.add_optional_argument<char>("method", "m").choices({'a', 'b', 'c'});
// passing a value other than a, b or c for the `method` argument will result in an errorImportant
- The
choicesfunction can be used only if the argument'svalue_typeis equality comparable (defines the==operator) - The
choicesfunction can be called with:- A variadic number of values convertible to the argument's value type
- An arbitrary
std::ranges::rangetype with a value type convertible to the argument's value type
Actions are represented as functions, which take the argument's value as an argument. The available action types are:
-
observeactions |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.void is_valid_user_tag(const std::string& tag) { if (tag.empty() or tag.front() != '@') throw std::runtime_error(std::format("Invalid user tag: `{}` — must start with '@'", tag)); } parser.add_optional_argument<std::string>("user", "u") .action<ap::action_type::observe>(is_valid_user_tag);
-
transformactions |value_type(const value_type&)- applied to the parsed value. The returned value will be used to initialize the argument's value.std::string to_lower(std::string s) { for (auto& c : s) c = static_cast<char>(std::tolower(c)); return s; } parser.add_optional_argument<std::string>("key", "k") .action<ap::action_type::transform>(to_lower);
-
modifyactions |void(value_type&)- applied to the initialized value of an argument.void capitalize(std::string& s) { s.at(0) = std::toupper(s.at(0)); } parser.add_optional_argument<std::string>("name", "n") .action<ap::action_type::modify>(capitalize);
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.
8. default_values - A list of values which will be used if no values for an argument have been parsed
Warning
For both positional and optional arguments, setting the default_values parameter disables the required option.
// example: positional arguments
parser.add_positional_argument("input");
parser.add_positional_argument("output").default_values("output.txt");
parser.parse_args(argc, argv);
// `input` is guaranteed to have a value if parsing was successfull
const auto data = read_data(parser.value("input"));
// `output` is guaranteed to have a value even if one was not specified in the command-line
std::ofstream os(parser.value("output"));
os << data << std::endl;
/*
Command Result
-----------------------------------------------------------------------------------------
./program Parsing error (no value for the input argument)
./program input.txt Parsing success; Printing data to `output.txt`
./program input.txt myfile.txt Parsing success; Printing data to the `myfile.txt` file// example: optional arguments
parser.add_optional_argument("input", "i").required();
parser.add_optional_argument("output", "o").default_values("output.txt");
parser.parse_args(argc, argv);
// `input` is guaranteed to have a value if parsing was successfull
const auto data = read_data(parser.value("input"));
// `output` is guaranteed to have a value even if one was not specified in the command-line
std::ofstream os(parser.value("output"));
os << data << std::endl;
/*
Command Result
-----------------------------------------------------------------------------------------------
./program Parsing error (no value for the input argument)
./program --input input.txt Parsing success; Printing data to `output.txt`
./program -i input.txt -o myfile.txt Parsing success; Printing data to the `myfile.txt` fileNote
The default_values function can be called with:
- A variadic number of values convertible to the argument's value type
- An arbitrary
std::ranges::rangetype with a value type convertible to the argument's value type
Apart from the common parameters listed above, for optional arguments you can also specify the following parameters:
1. On-flag actions - For optional arguments, apart from value actions, you can specify on-flag actions which are executed immediately after parsing an argument's flag.
void print_debug_info() noexcept {
#ifdef NDEBUG
std::cout << "Running in release mode.\n";
#else
std::cout << "Running in debug mode.\n";
#endif
std::exit(EXIT_SUCCESS);
};
parser.add_optional_argument("--debug-info")
.action<ap::action_type::on_flag>(print_debug_info);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.
2. implicit_values - A list of values which will be set for an argument if only its flag but no values are parsed from the command-line.
// example
parser.add_optional_argument("save", "s").implicit_values("output.txt");
parser.parse_args(argc, argv);
const auto data = get_data(); // arbitrary program data
// `output` is not guaranteed to have a value
if (parser.has_value("save")) {
std::ofstream os(parser.value("save"));
os << data << std::endl;
}
/*
Command Result
--------------------------------------------------------------------
./program No data will be saved
./program -s The data will be saved to `output.txt`
./program --save myfile.txt The data will be saved to `myfile.txt`Note
The implicit_values function can be called with:
- A variadic number of values convertible to the argument's value type
- An arbitrary
std::ranges::rangetype with a value type convertible to the argument's value type
Tip
The implicit_values parameter is extremely useful when combined with default value (e.g. in case of boolean flags - see Adding Arguments).
-
print_help| on-flagPrints the parser's help message to the output stream and optionally exits with the given code.
typename ap::action_type::on_flag::type print_help( const ap::argument_parser& parser, const std::optional<int> exit_code = std::nullopt, std::ostream& os = std::cout ) noexcept;
-
check_file_exists| observe (value type:std::string)Throws if the provided file path does not exist.
ap::action::util::callable_type<ap::action_type::observe, std::string> check_file_exists() noexcept;
-
gt| observe (value type: arithmetic)Validates that the value is strictly greater than
lower_bound.template <ap::util::c_arithmetic T> ap::action::util::callable_type<ap::action_type::observe, T> gt(const T lower_bound) noexcept;
-
geq| observe (value type: arithmetic)Validates that the value is greater than or equal to
lower_bound.template <ap::util::c_arithmetic T> ap::action::util::callable_type<ap::action_type::observe, T> geq(const T lower_bound) noexcept;
-
lt| observe (value type: arithmetic)Validates that the value is strictly less than
upper_bound.template <ap::util::c_arithmetic T> ap::action::util::callable_type<ap::action_type::observe, T> lt(const T upper_bound) noexcept
-
leq| observe (value type: arithmetic)Validates that the value is less than or equal to
upper_bound.template <ap::util::c_arithmetic T> ap::action::util::callable_type<ap::action_type::observe, T> leq(const T upper_bound) noexcept
-
within| observe (value type: arithmetic)Checks if the value is within the given interval. Bound inclusivity is customizable using template parameters.
template <ap::util::c_arithmetic T, bool LeftInclusive = true, bool RightInclusive = true> ap::action::util::callable_type<ap::action_type::observe, T> within( const T lower_bound, const T upper_bound ) noexcept
The CPP-AP library defines several default arguments, which can be added to the parser's configuration as follows.
parser.default_arguments(<args>);Note
The default_arguments function can be called with:
- A variadic number of
ap::default_argumentvalues - An arbitrary
std::ranges::rangetype with theap::default_argumentvalue type
-
p_input:// equivalent to: parser.add_positional_argument<std::string>("input") .action<ap::action_type::modify>(ap::action::check_file_exists()) .help("Input file path");
-
p_output:// equivalent to: parser.add_positional_argument("output").help("Output file path");
-
o_help:// equivalent to: parser.add_optional_argument<ap::none_type>("help", "h") .action<action_type::on_flag>(ap::action::print_help(parser, EXIT_SUCCESS)) .help("Display the help message");
-
o_inputando_multi_input:// input - equivalent to: parser.add_optional_argument("input", "i") .required() .nargs(1) .action<ap::action_type::observe>(ap::action::check_file_exists()) .help("Input file path"); // multi_input - equivalent to: parser.add_optional_argument("input", "i") .required() .nargs(ap::nargs::at_least(1)) .action<ap::action_type::observe>(ap::action::check_file_exists()) .help("Input files paths");
-
o_outputando_multi_output:// output - equivalent to: parser.add_optional_argument("output", "o") .required() .nargs(1) .help("Output file path"); // multi_otput - equivalent to: parser.add_optional_argument("output", "o") .required() .nargs(ap::nargs::at_least(1)) .help("Output files paths");
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));Tip
The parse_args function may throw an ap::argument_parser_exception (specifically the ap::parsing_failure derived exception) if the provided command-line arguments do not match the expected configuration. To simplify error handling, the argument_parser class provides a try_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(...);
}
catch (const ap::argument_parser_exception& err) {
std::cerr << "[ERROR] : " << err.what() << std::endl << parser << std::endl;
std::exit(EXIT_FAILURE);
}The simple example below demonstrates how (in terms of the program's structure) the argument parsing should look like.
// include the main library header
#include <ap/argument_parser.hpp>
int main(int argc, char* argv[]) {
// create the parser class instance
ap::argument_parser parser;
// define the parser's attributes
parser.program_name("some-program")
.program_description("The program does something with command-line arguments");
// define the program arguments
parser.add_positional_argument("positional").help("A positional argument");
parser.add_optional_argument("optional", "o").help("An optional argument");
parser.add_flag("flag", "f").help("A boolean flag");
parser.default_arguments(ap::default_argument::o_help);
// parse command-line arguments
parser.try_parse_args(argc, argv);
// use the program's arguments
std::cout << "positional: " << parser.value("positional") << std::endl
<< "optional: " << ap::util::join(parser.values("optional")) << std::endl
<< "flag: " << std::boolalpha << parser.value<bool>("flag") << std::endl;
return 0;
}An optional argument is recognized only when its primary or secondary flag appears in the command-line input. For example:
parser.add_optional_argument("optional", "o");Here, the argument is parsed only if either --optional (primary flag) or -o (secondary flag) is present. If neither flag is given, the argument is ignored.
Important
The parser will try to assign the values following such flag to the specified argument until:
- A different argument flag is encountered:
// program.cpp
parser.add_optional_argument("first", "f");
parser.add_optional_argument("second", "s");
parser.try_parse_args(argc, argv);
std::cout << "first: " << ap::util::join(parser.values("first")) << std::endl
<< "second: " << ap::util::join(parser.values("second")) << std::endl;
/* Example execution:
> ./program --first value1 value2 --second value3 value4
first: value1, value2
second: value3, value4- The upper bound of the argument's nargs parameter is reached:
NOTE: By default an optional argument accepts an arbitrary number of values (the number of values has no upper bound).
parser.add_optional_argument<int>("numbers", "n")
.nargs(ap::nargs::up_to(3))
.help("A list of numbers");> ./program --numbers 1 2 3 4 5
[ERROR] : Failed to deduce the argument for values [4, 5]
Program: program
An example program
Optional arguments:
--help, -h : Display the help message
--numbers, -n : A list of numbersPositional arguments are assigned values in the same order they are defined in the program. They are parsed from the command-line input excluding any values that have already been consumed by optional arguments. This means positional arguments no longer need to appear at the beginning of the argument list.
For example:
parser.add_positional_argument("positional1");
parser.add_positional_argument("positional2");
parser.try_parse_args(argc, argv);
std::cout << "positional1: " << parser.value("positional1") << std::endl
<< "positional2: " << parser.value("positional2") << std::endl;
/* Example execution:
> ./program value1 value2
positional1: value1
positional2: value2Important
- All positional arguments expect at most one value.
- A positional argument's value doesn't have to be preset in the command-line only if the argument is defined as not required.
A positional argument consumes only those values that cannot be assigned to optional arguments. This allows positional arguments to appear after optional arguments in the command-line input.
parser.add_positional_argument("positional1");
parser.add_positional_argument("positional2");
parser.add_optional_argument("optional").nargs(1); // limit the number of arguments
parser.try_parse_args(argc, argv);
std::cout << "positional1: " << parser.value("positional1") << std::endl
<< "positional2: " << parser.value("positional2") << std::endl
<< "optional: " << parser.value("optional") << std::endl;
/* Example executions:
> ./program pos1-value pos2-value --optional opt-value
positional1: pos1-value
positional2: pos2-value
optional: opt-value
> ./program --optional opt-value pos1-value pos2-value
positional1: pos1-value
positional2: pos2-value
optional: opt-value
> ./program pos1-value --optional opt-value pos2-value
positional1: pos1-value
positional2: pos2-value
optional: opt-valueTip
Because of the optional arguments accept an arbitrary number of arguments by default, it is a good practice to set the nargs parameter for optional arguments (where it makes sense).
A command-line argument beginning with a flag prefix (-- or -) that doesn't match any of the specified optional arguments or a compound of optional arguments (only for short flags) is considered unknown or unrecognized.
By default an argument parser will throw an exception if an unkown argument flag is encountered.
This behavior can be modified using the unknown_arguments_policy method of the argument_parser class, which sets the policy for handling unknown argument flags.
Example:
#include <ap/argument_parser.hpp>
int main(int argc, char* argv[]) {
ap::argument_parser parser;
parser.program_name("test")
.program_description("A simple test program")
.default_arguments(ap::default_argument::o_help)
// set the unknown argument flags handling policy
.unknown_arguments_policy(ap::unknown_policy::<policy>);
parser.add_optional_argument("known", "k")
.help("A known optional argument");
parser.try_parse_args(argc, argv);
std::cout << "known = " << ap::util::join(parser.values("known")) << std::endl;
return 0;
}The available policies are:
-
ap::unknown_policy::fail(default) - throws an exception if an unknown argument flag is encountered:> ./test --known --unknown [ap::error] Unknown argument [--unknown]. Program: test A simple test program Optional arguments: --help, -h : Display the help message --known, -k : A known optional argument
-
ap::unknown_policy::warn- prints a warning message to the standard error stream and continues parsing the remaining arguments:> ./test --known --unknown [ap::warning] Unknown argument '--unknown' will be ignored. known =
-
ap::unknown_policy::ignore- ignores unknown argument flags and continues parsing the remaining arguments:./test --known --unknown known =
-
ap::unknown_policy::as_values- treats unknown argument flags as values:> ./test --known --unknown known = --unknown
Important
- The unkown argument flags handling polciy only affects the parser's behaviour when calling the
parse_argsortry_parse_argsmethods. - When parsing known args with
parse_known_argsortry_parse_known_argsall unknown arguments (flags and values) are collected and returned as the parsing result, ignoring the specified policy for handling unknown arguments.
Consider a similar example as above with only the argument parsing function changed:
const auto unknown_args = parser.try_parse_known_args(argc, argv);
std::cout << "known = " << ap::util::join(parser.values("known")) << std::endl
<< "unknown = " << ap::util::join(unknown_args) << std::endl;This would produce the following output regardless of the specified unknown arguments policy.
> ./test --known --unknown
known =
unknown = --unknownCompound argument flags are secondary argument flags of which every character matches the secondary name of an optional argument.
Example:
parser.add_optional_argument("verbose", "v")
.nargs(0)
.help("Increase verbosity level");
parser.add_flag("option", "o")
.help("Enable an option flag");
parser.add_optional_argument<int>("numbers", "n")
.help("Provide integer values");
parser.try_parse_args(argc, argv);
std::cout << "Verbosity level: " << parser.count("verbose")
<< "\nOption used: " << std::boolalpha << parser.value<bool>("use-option")
<< "\nNumbers: " << ap::util::join(parser.values<int>("numbers"), ", ")
<< std::endl;
/*
> ./program -vvon 1 2 3
Verbosity level: 2
Option used: true
Numbers: 1, 2, 3Important
- 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:
parser.add_optional_argument("recognized", "r")
.nargs(ap::nargs::up_to(2))
.help("A recognized optional argument");
parser.parse_args(argc, argv);
std::cout << "recognized = " << ap::util::join(parser.values("recognized")) << std::endl;
/* Example executions:
> ./program --recognized value1 value2
recognized = value1, value2
> ./program --recognized value1 value2 value3
terminate called after throwing an instance of 'ap::parsing_failure'
what(): Failed to deduce the argument for values [value3]
Aborted (core dumped)
> ./program value0 --recognized value1 value2
terminate called after throwing an instance of 'ap::parsing_failure'
what(): Failed to deduce the argument for values [value0]
Aborted (core dumped)
> ./program --recognized value1 value2 --unrecognized value
terminate called after throwing an instance of 'ap::parsing_failure'
what(): Unknown argument [--unrecognized].
Aborted (core dumped)
>Here the parser throws exceptions for arguments it doesn't recognize. Now consider the same example with parse_known_args:
parser.add_optional_argument("recognized", "r")
.nargs(ap::nargs::up_to(2))
.help("A recognized optional argument");
const auto unknown_args = parser.parse_known_args(argc, argv);
std::cout << "recognized = " << ap::util::join(parser.values("recognized")) << std::endl
<< "unkown = " << ap::util::join(unknown_args) << std::endl;
/* Example execution:
> ./program value0 --recognized value1 value2 value3 --unrecognized value
recognized = value1, value2
unkown = value0, value3, --unrecognized, valueNow 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 used to parse positional arguments or treated as an unknown argument as well (if there are no unparsed positional arguments). Let's consider an example:
parser.add_positional_argument("positional")
.help("A positinal argument");
parser.add_optional_argument("recognized", "r")
.nargs(ap::nargs::any())
.help("A recognized optional argument");
const auto unknown_args = parser.parse_known_args(argc, argv);
std::cout << "positional = " << parser.value("positional") << std::endl
<< "recognized = " << ap::util::join(parser.values("recognized")) << std::endl
<< "unkown = " << ap::util::join(unknown_args) << std::endl;
/* Example execution:
> ./program --recognized value1 value2 value3 --unrecognized value4 value5 --recognized value6
positional = value4
recognized = value1, value2, value3, value6
unkown = --unrecognized, value5
> ./program value0 --recognized value1 value2 value3 --unrecognized value4 --recognized value5
positional = value0
recognized = value1, value2, value3, value5
unkown = --unrecognized, value4Here value is treated either as the positional argument's value or as an unknown argument (depending on the input arguments) even though the recognized optional argument still accepts values and only after the --recognized argument flag is encountered the parser continues collecting values for this argument.
Tip
Similarly to the parse_args method, parse_known_args has a try 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(s) with:
(const) value_type value = parser.value<value_type>("argument_name"); // (1)
(const) value_type value = parser.value_or<value_type>("argument_name", fallback_value); // (2)
(const) std::vector<value_type> values = parser.values<value_type>("argument_name"); // (3)-
Returns the given argument's value.
- Returns the argument's parsed value if it has one.
- If more than one value has been parsed for the argument, this function will return the first parsed value.
- Returns the argument's predefined value if no value has been parsed for the argument.
-
Returns the given argument's value or the specified fallback value if the argument has no values.
- If the argument has a value (parsed or predefind), the behavior is the same as in case (1).
- If the argument has no values, this will return
value_type{std::forward<U>(fallback_value)}(whereUis the deduced type offallback_value).
-
Returns a vector of the given argument's values.
- If the argument has any values (parsed or predefined), they will be returned as a
std::vector<value_type>. - If th argument has no values an empty vector will be returned.
- If the argument has any values (parsed or predefined), they will be returned as a
Note
The argument value getter functions might throw an exception if:
- An argument with the given name does not exist
- The argument does not contain any values - parsed or predefined (only getter function
(1)) - The specified
value_typedoes not match the value type of the argument
The library usage examples / demo projects can be found in the cpp-ap-demo repository.
The CPP-AP library provides some additional utility, the descriptions of which can be found on the Utility topic page.