CPP-AP 2.7.0
Command-line argument parser for C++20
Loading...
Searching...
No Matches
Tutorial


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.


Setting Up CPP-AP

CMake Integration

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)

Bazel Build System

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"],
)

Downloading the Library

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.




The Parser Class

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 - 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.
  • Arguments - specify the values/options accepted by the program.
parser.program_name("Name of the program")
.program_version("alhpa")
.program_description("Description of the program")
.verbose();
Main argument parser class.
argument_parser & program_version(const version &version) noexcept
Set the program version.
argument_parser & program_description(std::string_view description) noexcept
Set the program description.
argument_parser & program_name(std::string_view name) noexcept
Set the program name.
argument_parser & verbose(const bool v=true) noexcept
Set the verbosity mode.

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);
A helper structure used to represent a program's version.
Definition version.hpp:13

NOTE: The ap::version struct

  • contains the three members - major, minor, patch - all of which are of type std::uint32_t,
  • defines a std::string str() const method which returns a v{major}.{minor}.{path} version string,
  • defines the std::ostream& operator<< for stream insertion.




Adding Arguments

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:

parser.add_<positional/optional>_argument<value_type>("argument");

or

parser.add_<positional/optional>_argument<value_type>("argument", "a");

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 from std::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:

parser.add_flag("enable_some_option", "eso").help("enables option: some option");
/* equivalent to:
parser.add_optional_argument<bool>("enable_some_option", "eso")
.default_value(false)
.implicit_value(true)
.nargs(0)
.help("enables option: some option");
*/
argument::optional< bool > & add_flag(const std::string_view name, const detail::argument_name_discriminator name_discr=n_primary)
Adds a boolean flag argument (an optional argument with value_type = bool) to the parser's configurat...

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_value(true)
.implicit_value(false)
.nargs(0)
.help("disables option: another option");
*/

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.




Argument Parameters

Common Parameters

Parameters which can be specified for both positional and optional arguments include:

1. help - The argument's description which will be printed when printing the parser class instance.

parser.add_positional_argument<std::size_t>("number", "n")
.help("a positive integer value");
argument::positional< T > & add_positional_argument(const std::string_view primary_name)
Adds a positional argument to the parser's configuration.

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")
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 argument
argument_parser & default_optional_arguments(const AR &arg_discriminator_range) noexcept
Set default optional arguments.
void try_parse_args(int argc, char *argv[])
Parses the command-line arguments and exits on error.

3. 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 is true 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 the bypass_required option
    • disabling the required option has no effect on the bypass_required option.
// 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
T value(std::string_view arg_name) const
bool has_value(std::string_view arg_name) const noexcept
void parse_args(int argc, char *argv[])
Parses the command-line arguments.
// 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_required option disabled.
  • The default value of the value parameter of the bypass_required(bool) function is true for both positional and optional arguments.

Warning:

For both positional and optional arguments:

  • enabling the bypass_required option disables the required option
  • disabling the bypass_required option has no effect on the required option.
// 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;
std::size_t count(std::string_view arg_name) const noexcept

5. 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 the required option.

// example: positional arguments
parser.add_positional_argument("input");
parser.add_positional_argument("output").default_value("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_value("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` file

Tip:

The setter of the default_value parameter accepts any type that is convertible to the argument's value type.

6. 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.

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 error

Important:

The choices function can be used only if the argument's value_type is equality comparable (defines the == operator).

7. Value actions - Function performed after parsing an argument's value.

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.

    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);
  • transform actions | 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);
  • modify actions | 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.


Parameters Specific for Optional Arguments

Apart from the common parameters listed above, for optional arguments you can also specify the following parameters:

1. nargs - Sets the allowed number of values to be parsed for an argument. This can be set as a:

Important:

The default nargs parameter value is ap::nargs::any().

2. 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.

// example
parser.add_optional_argument("save", "s").implicit_value("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`

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.

4. 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);
An on-flag action specifier.

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.




Predefined Parameter Values

Actions

  • print_config | on-flag

    Prints the configuration of the parser to the output stream and optionally exits with the given code.

    typename ap::action_type::on_flag::type print_config(
    const ap::argument_parser& parser,
    const std::optional<int> exit_code = std::nullopt,
    std::ostream& os = std::cout
    ) noexcept;
    std::function< void()> type
  • check_file_exists | observe (value type: std::string)

    Throws if the provided file path does not exist.

    detail::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::detail::c_arithmetic T>
    detail::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::detail::c_arithmetic T>
    detail::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::detail::c_arithmetic T>
    detail::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::detail::c_arithmetic T>
    detail::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::detail::c_arithmetic T, bool LeftInclusive = true, bool RightInclusive = true>
    detail::callable_type<ap::action_type::observe, T> within(
    const T lower_bound, const T upper_bound
    ) noexcept




Default Arguments

The CPP-AP library defines several default arguments, which can be added to the parser's configuration as follows.

// here `...` represents a collection of ap::argument::default_positional values
// here `...` represents a collection of ap::argument::default_optional values
argument_parser & default_positional_arguments(const AR &arg_discriminator_range) noexcept
Set default positional arguments.

Note:

These functions work with std::initializer_list and all other std::ranges::range types with the correct value type - ap::argument::default_{positional/optional}

The available default arguments are:




Parsing Arguments

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:

parser.add_optional_argument("option", "o");
parser.try_parse_args(argc, argv);
std::cout << "option: " << parser.value("option");
/*
./program --option --unknown-flag
option: --unknown-flag

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_VALUES

before the #include <ap/argument_parser.hpp> statement.

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 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);
}
Base type for the argument parser functionality errors/exceptions.
// power.cpp
#include <cmath>
#include <iostream>
int main(int argc, char* argv[]) {
// create the parser class instance
parser.program_name("power calculator")
.program_description("Calculates the value of an expression: base ^ exponent");
// add arguments
parser.add_positional_argument<double>("base").help("the exponentation base value");
parser.add_optional_argument<int>("exponent", "e")
.nargs(ap::nargs::any())
.help("the exponent value");
// parse command-line arguments
parser.try_parse_args(argc, argv);
// check if any values for the `exponent` argument have been parsed
if (not parser.has_value("exponent")) {
std::cout << "no exponent values given" << std::endl;
std::exit(EXIT_SUCCESS);
}
const double base = parser.value<double>("base");
const std::vector<int> exponent_values = parser.values<int>("exponent");
for (const int exponent : exponent_values) {
std::cout << base << " ^ " << exponent << " = " << std::pow(base, exponent) << std::endl;
}
return 0;
}
// compiled with:
// g++ -o power power.cpp -I <cpp-ap-include-dir> -std=c++20
Main library header file. Defines the argument_parser class.
std::vector< T > values(std::string_view arg_name) const

Argument Parsing Rules

  • 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:

    ./power 2
    no exponent values given
    ./power
    [ERROR] : No values parsed for a required argument [base]
    Program: power calculator
    Calculates the value of an expression: base ^ exponent
    Positional arguments:
    base : the exponentation base value
    Optional arguments:
    --exponent, -e : the exponent value
    --help, -h : Display the help message

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.

    ./power 2 --exponent 1 2 3 # equivalent to: ./power 2 -e 1 2 3
    2 ^ 1 = 2
    2 ^ 2 = 4
    2 ^ 3 = 8

    You can use the flag for each command-line value:

    ./power 2 -e 1 -e 2 -e 3

    Not using a flag will result in an error:

    ./power 2 1 2 3
    [ERROR] : Failed to deduce the argument for values [1, 2, 3]
    Program: power calculator
    Calculates the value of an expression: base ^ exponent
    Positional arguments:
    base : the exponentation base value
    Optional arguments:
    --exponent, -e : the exponent value
    --help, -h : Display the help message

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:

parser.add_optional_argument<int>("exponent", "e").nargs(ap::nargs::up_to(3))
./power 2 -e 1 2 3 4 5
[ERROR] : Failed to deduce the argument for values [4, 5]
Program: power calculator
Calculates the value of an expression: base ^ exponent
Positional arguments:
base : the exponentation base value
Optional arguments:
--exponent, -e : the exponent value
--help, -h : Display the help message


Compound Arguments

Compound 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: " << join(parser.values<int>("numbers"), ", ") // join is an imaginary function :)
<< std::endl;
/*
> ./program -vvon 1 2 3
Verbosity level: 2
Option used: true
Numbers: 1, 2, 3

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.


Parsing Known Arguments

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.try_parse_args(argc, argv);
std::cout << "recognized = " << 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 = " << join(parser.values("recognized")) << std::endl
<< "unkown = " << join(unknown_args) << std::endl;
/* Example execution:
./program value0 --recognized value1 value2 value3 --unrecognized value
recognized = value1, value2
unkown = value0, value3, --unrecognized, value
std::vector< std::string > parse_known_args(int argc, char *argv[])
Parses the known command-line arguments.

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:

parser.add_optional_argument("recognized", "r")
.nargs(ap::nargs::any()) // don't restrict the number of arguments
.help("A recognized optional argument");
const auto unknown_args = parser.parse_known_args(argc, argv);
std::cout << "recognized = " << join(parser.values("recognized")) << std::endl
<< "unkown = " << join(unknown_args) << std::endl;
/* Example execution:
./program value0 --recognized value1 value2 value3 --unrecognized value --recognized value4
recognized = value1, value2, value3, value4
unkown = value0, --unrecognized, value

Here value is treated as an unknown argument even though the recognized 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 a try equivalent - try_parse_known_args - which will automatically catch these exceptions, print the error message, and exit with a failure status.




Retrieving Argument Values

You can retrieve the argument's value with:

(const) auto value = parser.value<value_type>("argument_name"); // (1)
(const) auto value = parser.value_or<value_type>("argument_name", fallback_value); // (2)
T value_or(std::string_view arg_name, U &&fallback_value) const
  1. 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.

  2. When a value has been parsed for the argument, the behavior is the same as in case **(1)**. Otherwise, this will return value_type{std::forward<U>(fallback_value)} (where U is the deducted type of fallback_value), if:
    • There is no value parsed for a positional argument
    • There is no parsed values and no predefined values for an optional arrument


Additionally for optional arguments, you can use:

(const) std::vector<value_type> values = parser.values<value_type>("argument_name");

which returns a vector containing all values parsed for the given argument.




Examples

The library usage examples / demo projects can be found in the cpp-ap-demo repository.