CPP-AP 3.0.1
Command-line argument parser for C++20
Loading...
Searching...
No Matches
/home/runner/work/cpp-ap/cpp-ap/include/ap/argument_parser.hpp

Returns the parser's full program name.

Returns the parser's full program name.- For top-level parsers, this is the same as the parser's name.

Top-level parser: git Subparser: git submodule Nested subparser : git submodule init

// Copyright (c) 2023-2025 Jakub Musiał
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
#pragma once
#include "argument.hpp"
#include "types.hpp"
#include <algorithm>
#include <format>
#include <iostream>
#include <ranges>
#include <span>
#ifdef AP_TESTING
namespace ap_testing {
struct argument_parser_test_fixture;
} // namespace ap_testing
#endif
namespace ap {
class argument_parser;
enum class default_argument : std::uint8_t {
};
enum class unknown_policy : std::uint8_t {
fail,
warn,
};
namespace detail {
void add_default_argument(const default_argument, argument_parser&) noexcept;
} // namespace detail
class argument_parser {
public:
argument_parser(const argument_parser&) = delete;
argument_parser& operator=(const argument_parser&) = delete;
argument_parser(argument_parser&&) = delete;
argument_parser& operator=(argument_parser&&) = delete;
argument_parser(const std::string_view name) : argument_parser(name, "") {}
~argument_parser() = default;
argument_parser& program_version(const version& version) noexcept {
this->_program_version.emplace(version.str());
return *this;
}
argument_parser& program_version(std::string_view version) {
throw invalid_configuration("The program version cannot contain whitespace characters!"
);
this->_program_version.emplace(version);
return *this;
}
argument_parser& program_description(std::string_view description) noexcept {
this->_program_description.emplace(description);
return *this;
}
argument_parser& verbose(const bool v = true) noexcept {
this->_verbose = v;
return *this;
}
this->_unknown_policy = policy;
return *this;
}
template <util::c_range_of<default_argument> AR>
argument_parser& default_arguments(const AR& arg_discriminators) noexcept {
for (const auto arg_discriminator : arg_discriminators)
detail::add_default_argument(arg_discriminator, *this);
return *this;
}
const std::initializer_list<default_argument>& arg_discriminators
) noexcept {
}
const std::same_as<default_argument> auto... arg_discriminators
) noexcept {
return *this;
}
template <util::c_argument_value_type T = std::string>
}
template <util::c_argument_value_type T = std::string>
argument_group& group, const std::string_view name
) {
this->_validate_group(group);
const detail::argument_name arg_name(std::make_optional<std::string>(name));
auto& new_arg_ptr =
this->_positional_args.emplace_back(std::make_shared<positional_argument<T>>(arg_name));
group._add_argument(new_arg_ptr);
return static_cast<positional_argument<T>&>(*new_arg_ptr);
}
template <util::c_argument_value_type T = std::string>
const std::string_view name,
) {
}
template <util::c_argument_value_type T = std::string>
const std::string_view primary_name, const std::string_view secondary_name
) {
this->_gr_optional_args, primary_name, secondary_name
);
}
template <util::c_argument_value_type T = std::string>
argument_group& group,
const std::string_view name,
) {
this->_validate_group(group);
const auto arg_name =
? detail::
argument_name{std::make_optional<std::string>(name), std::nullopt, this->_flag_prefix_char}
: detail::argument_name{
std::nullopt, std::make_optional<std::string>(name), this->_flag_prefix_char
};
auto& new_arg_ptr =
this->_optional_args.emplace_back(std::make_shared<optional_argument<T>>(arg_name));
group._add_argument(new_arg_ptr);
return static_cast<optional_argument<T>&>(*new_arg_ptr);
}
template <util::c_argument_value_type T = std::string>
argument_group& group,
const std::string_view primary_name,
const std::string_view secondary_name
) {
this->_validate_group(group);
this->_verify_arg_name_pattern(primary_name);
this->_verify_arg_name_pattern(secondary_name);
const detail::argument_name arg_name(
std::make_optional<std::string>(primary_name),
std::make_optional<std::string>(secondary_name),
);
auto& new_arg_ptr =
this->_optional_args.emplace_back(std::make_shared<optional_argument<T>>(arg_name));
group._add_argument(new_arg_ptr);
return static_cast<optional_argument<T>&>(*new_arg_ptr);
}
template <bool StoreImplicitly = true>
const std::string_view name,
) {
.default_values(not StoreImplicitly)
.implicit_values(StoreImplicitly)
.nargs(0ull);
}
template <bool StoreImplicitly = true>
const std::string_view primary_name, const std::string_view secondary_name
) {
return this->add_optional_argument<bool>(primary_name, secondary_name)
.default_values(not StoreImplicitly)
.implicit_values(StoreImplicitly)
.nargs(0ull);
}
template <bool StoreImplicitly = true>
argument_group& group,
const std::string_view name,
) {
.default_values(not StoreImplicitly)
.implicit_values(StoreImplicitly)
.nargs(0ull);
}
template <bool StoreImplicitly = true>
argument_group& group,
const std::string_view primary_name,
const std::string_view secondary_name
) {
.default_values(not StoreImplicitly)
.implicit_values(StoreImplicitly)
.nargs(0ull);
}
argument_group& add_group(const std::string_view name) noexcept {
return *this->_argument_groups.emplace_back(argument_group::create(*this, name));
}
argument_parser& add_subparser(const std::string_view name) {
const auto subparser_it = std::ranges::find(
this->_subparsers, name, [](const auto& subparser) { return subparser->_name; }
);
if (subparser_it != this->_subparsers.end())
throw std::logic_error(std::format(
"A subparser with the given name () already exists in parser '{}'",
(*subparser_it)->_name,
));
return *this->_subparsers.emplace_back(
std::unique_ptr<argument_parser>(new argument_parser(name, this->_program_name))
);
}
void parse_args(int argc, char* argv[]) {
this->parse_args(std::span(argv + 1, static_cast<std::size_t>(argc - 1)));
}
template <util::c_forward_range_of<std::string, util::type_validator::convertible> AR>
void parse_args(const AR& argv_rng) {
parsing_state state(*this);
this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state);
if (not state.unknown_args.empty())
throw parsing_failure(std::format(
"Failed to deduce the argument for values [{}]", util::join(state.unknown_args)
));
}
void try_parse_args(int argc, char* argv[]) {
this->try_parse_args(std::span(argv + 1, static_cast<std::size_t>(argc - 1)));
}
template <util::c_forward_range_of<std::string, util::type_validator::convertible> AR>
void try_parse_args(const AR& argv_rng) {
try {
this->parse_args(argv_rng);
}
std::cerr << "[ap::error] " << err.what() << std::endl
<< this->resolved_parser() << std::endl;
std::exit(EXIT_FAILURE);
}
}
std::vector<std::string> parse_known_args(int argc, char* argv[]) {
return this->parse_known_args(std::span(argv + 1, static_cast<std::size_t>(argc - 1)));
}
template <util::c_forward_range_of<std::string, util::type_validator::convertible> AR>
std::vector<std::string> parse_known_args(const AR& argv_rng) {
parsing_state state(*this, true);
this->_parse_args_impl(std::ranges::begin(argv_rng), std::ranges::end(argv_rng), state);
return std::move(state.unknown_args);
}
std::vector<std::string> try_parse_known_args(int argc, char* argv[]) {
return this->try_parse_known_args(std::span(argv + 1, static_cast<std::size_t>(argc - 1)));
}
template <util::c_forward_range_of<std::string, util::type_validator::convertible> AR>
std::vector<std::string> try_parse_known_args(const AR& argv_rng) {
try {
return this->parse_known_args(argv_rng);
}
std::cerr << "[ap::error] " << err.what() << std::endl
<< this->resolved_parser() << std::endl;
std::exit(EXIT_FAILURE);
}
}
[[nodiscard]] std::string_view name() const noexcept {
return this->_name;
}
[[nodiscard]] std::string_view program_name() const noexcept {
return this->_program_name;
}
return this->_invoked;
}
return this->_finalized;
}
const auto used_subparser_it = std::ranges::find_if(
this->_subparsers, [](const auto& subparser) { return subparser->_invoked; }
);
if (used_subparser_it == this->_subparsers.end())
return *this;
return (*used_subparser_it)->resolved_parser();
}
[[nodiscard]] bool is_used(std::string_view arg_name) const noexcept {
const auto arg = this->_get_argument(arg_name);
return arg ? arg->is_used() : false;
}
[[nodiscard]] bool has_value(std::string_view arg_name) const noexcept {
const auto arg = this->_get_argument(arg_name);
return arg ? arg->has_value() : false;
}
[[nodiscard]] std::size_t count(std::string_view arg_name) const noexcept {
const auto arg = this->_get_argument(arg_name);
return arg ? arg->count() : 0ull;
}
template <util::c_argument_value_type T = std::string>
[[nodiscard]] T value(std::string_view arg_name) const {
const auto arg = this->_get_argument(arg_name);
if (not arg)
const auto& arg_value = arg->value();
try {
return std::any_cast<T>(arg_value);
}
catch (const std::bad_any_cast&) {
throw type_error::invalid_value_type<T>(arg->name());
}
}
template <util::c_argument_value_type T = std::string, std::convertible_to<T> U>
[[nodiscard]] T value_or(std::string_view arg_name, U&& fallback_value) const {
const auto arg = this->_get_argument(arg_name);
if (not arg)
try {
const auto& arg_value = arg->value();
return std::any_cast<T>(arg_value);
}
catch (const std::logic_error&) {
// positional: no value parsed
// optional: no value parsed + no predefined value
return T{std::forward<U>(fallback_value)};
}
catch (const std::bad_any_cast&) {
throw type_error::invalid_value_type<T>(arg->name());
}
}
template <util::c_argument_value_type T = std::string>
[[nodiscard]] std::vector<T> values(std::string_view arg_name) const {
const auto arg = this->_get_argument(arg_name);
if (not arg)
try {
std::vector<T> values;
// TODO: use std::ranges::to after transition to C++23
std::ranges::copy(
util::any_range_cast_view<T>(arg->values()), std::back_inserter(values)
);
return values;
}
catch (const std::bad_any_cast&) {
throw type_error::invalid_value_type<T>(arg->name());
}
}
void print_help(const bool verbose, std::ostream& os = std::cout) const noexcept {
os << "Program: " << this->_program_name;
if (this->_program_version)
os << " (" << this->_program_version.value() << ')';
os << '\n';
os << '\n'
<< std::string(this->_indent_width, ' ') << this->_program_description.value()
<< '\n';
for (const auto& group : this->_argument_groups)
}
void print_version(std::ostream& os = std::cout) const noexcept {
os << this->_program_name << " : version " << this->_program_version.value_or("unspecified")
<< std::endl;
}
friend std::ostream& operator<<(std::ostream& os, const argument_parser& parser) noexcept {
return os;
}
#ifdef AP_TESTING
friend struct ::ap_testing::argument_parser_test_fixture;
#endif
private:
using arg_ptr_t = std::shared_ptr<detail::argument_base>;
using arg_ptr_vec_t = std::vector<arg_ptr_t>;
using arg_ptr_vec_iter_t = typename arg_ptr_vec_t::iterator;
using arg_group_ptr_t = std::unique_ptr<argument_group>;
using arg_group_ptr_vec_t = std::vector<arg_group_ptr_t>;
using arg_parser_ptr_t = std::unique_ptr<argument_parser>;
using arg_parser_ptr_vec_t = std::vector<arg_parser_ptr_t>;
using arg_token_vec_t = std::vector<detail::argument_token>;
using arg_token_vec_iter_t = typename arg_token_vec_t::const_iterator;
struct parsing_state {
parsing_state(argument_parser& parser, const bool parse_known_only = false)
void set_parser(argument_parser& parser) {
this->curr_arg = nullptr;
this->curr_pos_arg_it = parser._positional_args.begin();
}
const bool
std::vector<std::string> unknown_args = {};
};
argument_parser(const std::string_view name, const std::string_view parent_name)
: _name(name),
std::format("{}{}{}", parent_name, std::string(not parent_name.empty(), ' '), name)
),
_gr_positional_args(add_group("Positional Arguments")),
_gr_optional_args(add_group("Optional Arguments")) {
if (name.empty())
throw invalid_configuration("The program name cannot be empty!");
throw invalid_configuration("The program name cannot contain whitespace characters!");
}
void _verify_arg_name_pattern(const std::string_view arg_name) const {
if (arg_name.empty())
arg_name, "An argument name cannot be empty."
);
arg_name, "An argument name cannot contain whitespaces."
);
if (arg_name.front() == this->_flag_prefix_char)
std::format(
"An argument name cannot begin with a flag prefix character ({}).",
)
);
if (std::isdigit(arg_name.front()))
arg_name, "An argument name cannot begin with a digit."
);
}
const std::string_view arg_name,
) const noexcept {
return [=](const arg_ptr_t& arg) { return arg->name().match(arg_name, m_type); };
}
const detail::argument_name& arg_name,
) const noexcept {
return [&arg_name, m_type](const arg_ptr_t& arg) {
return arg->name().match(arg_name, m_type);
};
}
const detail::argument_name& arg_name,
) const noexcept {
const auto predicate = this->_name_match_predicate(arg_name, m_type);
if (std::ranges::find_if(this->_positional_args, predicate) != this->_positional_args.end())
return true;
if (std::ranges::find_if(this->_optional_args, predicate) != this->_optional_args.end())
return true;
return false;
}
void _validate_group(const argument_group& group) {
if (group._parser != this)
throw std::logic_error(std::format(
"An argument group '{}' does not belong to the given parser.", group._name
));
}
template <util::c_forward_iterator_of<std::string, util::type_validator::convertible> AIt>
void _parse_args_impl(AIt args_begin, const AIt args_end, parsing_state& state) {
this->_invoked = true;
if (args_begin != args_end) {
// try to match a subparser
const auto subparser_it =
std::ranges::find(this->_subparsers, *args_begin, [](const auto& subparser) {
return subparser->_name;
});
if (subparser_it != this->_subparsers.end()) {
state.set_parser(subparser);
return;
}
}
// process command-line arguments within the current parser
for (const auto& tok : this->_tokenize(args_begin, args_end, state))
this->_finalized = true;
}
// step 1
for (const auto& arg : this->_positional_args) {
if (not arg->is_required()) {
continue;
}
if (non_required_arg and arg->is_required())
throw invalid_configuration(std::format(
"Required positional argument [{}] cannot be defined after a non-required "
"positional argument [{}].",
arg->name().str(),
));
}
}
template <util::c_forward_iterator_of<std::string, util::type_validator::convertible> AIt>
AIt args_begin, const AIt args_end, const parsing_state& state
) {
toks.reserve(static_cast<std::size_t>(std::ranges::distance(args_begin, args_end)));
std::ranges::for_each(args_begin, args_end, [&](const auto& arg_value) {
this->_tokenize_arg(arg_value, toks, state);
});
return toks;
}
const std::string_view arg_value, arg_token_vec_t& toks, const parsing_state& state
) {
detail::argument_token tok{
.type = this->_deduce_token_type(arg_value), .value = std::string(arg_value)
};
if (not tok.is_flag_token() or this->_validate_flag_token(tok)) {
toks.emplace_back(std::move(tok));
return;
}
// not a value token -> flag token
// flag token could not be validated -> unknown flag
if (state.parse_known_only) { // do nothing (will be handled during parsing)
toks.emplace_back(std::move(tok));
return;
}
switch (this->_unknown_policy) {
std::cerr << "[ap::warning] Unknown argument '" << tok.value << "' will be ignored."
<< std::endl;
return;
toks.emplace_back(std::move(tok));
break;
}
}
const std::string_view arg_value
) const noexcept {
if (arg_value.starts_with(this->_flag_prefix))
if (arg_value.starts_with(this->_flag_prefix_char))
}
[[nodiscard]] bool _validate_flag_token(detail::argument_token& tok) noexcept {
const auto opt_arg_it = this->_find_opt_arg(tok);
if (opt_arg_it == this->_optional_args.end())
tok.args.emplace_back(*opt_arg_it);
return true;
}
bool _validate_compound_flag_token(detail::argument_token& tok) noexcept {
return false;
tok.args.reserve(actual_tok_value.size());
for (const char c : actual_tok_value) {
const auto opt_arg_it = std::ranges::find_if(
std::string_view(&c, 1ull), detail::argument_name::m_secondary
)
);
if (opt_arg_it == this->_optional_args.end()) {
tok.args.clear();
return false;
}
tok.args.emplace_back(*opt_arg_it);
}
return true;
}
[[nodiscard]] arg_ptr_vec_iter_t _find_opt_arg(const detail::argument_token& flag_tok
) noexcept {
if (not flag_tok.is_flag_token())
return this->_optional_args.end();
const auto match_type =
return std::ranges::find_if(
);
}
[[nodiscard]] std::string_view _strip_flag_prefix(const detail::argument_token& tok
) const noexcept {
switch (tok.type) {
return std::string_view(tok.value).substr(this->_primary_flag_prefix_length);
return std::string_view(tok.value).substr(this->_secondary_flag_prefix_length);
default:
return tok.value;
}
}
void _parse_token(const detail::argument_token& tok, parsing_state& state) {
if (state.curr_arg and state.curr_arg->is_greedy()) {
this->_set_argument_value(tok.value, state);
return;
}
if (tok.is_flag_token())
this->_parse_flag_token(tok, state);
else
}
void _parse_flag_token(const detail::argument_token& tok, parsing_state& state) {
if (not tok.is_valid_flag_token()) {
if (state.parse_known_only) {
state.curr_arg.reset();
state.unknown_args.emplace_back(tok.value);
return;
}
else {
// should never happen as unknown flags are filtered out during tokenization
}
}
for (const auto& arg : tok.args) {
if (arg->mark_used())
state.curr_arg = arg;
else
state.curr_arg.reset();
}
}
void _parse_value_token(const detail::argument_token& tok, parsing_state& state) {
if (not state.curr_arg) {
if (state.curr_pos_arg_it == this->_positional_args.end()) {
state.unknown_args.emplace_back(tok.value);
return;
}
state.curr_arg = *state.curr_pos_arg_it;
}
this->_set_argument_value(tok.value, state);
}
void _set_argument_value(const std::string_view value, parsing_state& state) noexcept {
if (state.curr_arg->set_value(std::string(value)))
return; // argument still accepts values
// advance to the next positional argument if possible
if (state.curr_arg->is_positional()
and state.curr_pos_arg_it != this->_positional_args.end())
++state.curr_pos_arg_it;
state.curr_arg.reset();
}
void _verify_final_state() const {
const auto [supress_group_checks, suppress_arg_checks] = this->_are_checks_suppressed();
for (const auto& group : this->_argument_groups)
}
[[nodiscard]] std::pair<bool, bool> _are_checks_suppressed() const noexcept {
bool suppress_group_checks = false;
bool suppress_arg_checks = false;
auto check_arg = [&](const arg_ptr_t& arg) {
if (arg->is_used()) {
if (arg->suppresses_group_checks())
suppress_group_checks = true;
if (arg->suppresses_arg_checks())
suppress_arg_checks = true;
}
};
// TODO: use std::views::join after the transition to C++23
std::ranges::for_each(this->_positional_args, check_arg);
std::ranges::for_each(this->_optional_args, check_arg);
return {suppress_group_checks, suppress_arg_checks};
}
const argument_group& group,
const bool suppress_group_checks,
const bool suppress_arg_checks
) const {
if (group._arguments.empty())
return;
if (not suppress_group_checks) {
const auto n_used_args = static_cast<std::size_t>(std::ranges::count_if(
group._arguments, [](const auto& arg) { return arg->is_used(); }
));
if (group._mutually_exclusive) {
if (n_used_args > 1ull)
throw parsing_failure(std::format(
"At most one argument from the mutually exclusive group '{}' can be used",
));
const auto used_arg_it = std::ranges::find_if(
group._arguments, [](const auto& arg) { return arg->is_used(); }
);
if (used_arg_it != group._arguments.end()) {
// only the one used argument has to be validated
this->_verify_argument_requirements(*used_arg_it, suppress_arg_checks);
return;
}
}
if (group._required and n_used_args == 0ull)
throw parsing_failure(std::format(
"At least one argument from the required group '{}' must be used", group._name
));
}
// all arguments in the group have to be validated
for (const auto& arg : group._arguments)
this->_verify_argument_requirements(arg, suppress_arg_checks);
}
void _verify_argument_requirements(const arg_ptr_t& arg, const bool suppress_arg_checks) const {
if (suppress_arg_checks)
return;
if (arg->is_required() and not arg->has_value())
throw parsing_failure(
std::format("No values parsed for a required argument [{}]", arg->name().str())
);
if (const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
}
arg_ptr_t _get_argument(std::string_view arg_name) const noexcept {
const auto predicate = this->_name_match_predicate(arg_name);
if (auto pos_arg_it = std::ranges::find_if(this->_positional_args, predicate);
pos_arg_it != this->_positional_args.end()) {
return *pos_arg_it;
}
if (auto opt_arg_it = std::ranges::find_if(this->_optional_args, predicate);
opt_arg_it != this->_optional_args.end()) {
return *opt_arg_it;
}
return nullptr;
}
void _print_subparsers(std::ostream& os) const noexcept {
if (this->_subparsers.empty())
return;
os << "\nCommands:\n";
std::vector<detail::help_builder> builders;
builders.reserve(this->_subparsers.size());
for (const auto& subparser : this->_subparsers)
for (const auto& bld : builders)
for (const auto& bld : builders)
os << '\n';
}
void _print_group(std::ostream& os, const argument_group& group, const bool verbose)
const noexcept {
auto visible_args = std::views::filter(group._arguments, [](const auto& arg) {
return not arg->is_hidden();
});
if (std::ranges::empty(visible_args))
return;
os << '\n' << group._name << ":";
std::vector<std::string> group_attrs;
if (group._required)
group_attrs.emplace_back("required");
if (group._mutually_exclusive)
group_attrs.emplace_back("mutually exclusive");
if (not group_attrs.empty())
os << " (" << util::join(group_attrs) << ')';
os << '\n';
if (verbose) {
for (const auto& arg : visible_args)
os << '\n' << arg->help_builder(verbose).get(this->_indent_width) << '\n';
}
else {
std::vector<detail::help_builder> builders;
builders.reserve(group._arguments.size());
for (const auto& arg : visible_args)
builders.emplace_back(arg->help_builder(verbose));
std::size_t max_arg_name_length = 0ull;
for (const auto& bld : builders)
for (const auto& bld : builders)
os << '\n' << bld.get_basic(this->_indent_width, max_arg_name_length);
os << '\n';
}
}
std::string _name;
std::string
std::optional<std::string> _program_version;
std::optional<std::string> _program_description;
bool _verbose = false;
argument_group& _gr_positional_args;
argument_group& _gr_optional_args;
bool _invoked =
false;
bool _finalized = false;
static constexpr std::uint8_t _primary_flag_prefix_length = 2u;
static constexpr std::uint8_t _secondary_flag_prefix_length = 1u;
static constexpr char _flag_prefix_char = '-';
static constexpr std::string_view _flag_prefix = "--";
static constexpr std::uint8_t _indent_width = 2;
};
namespace detail {
const default_argument arg_discriminator, argument_parser& arg_parser
) noexcept {
switch (arg_discriminator) {
arg_parser.add_positional_argument("input")
.action<action_type::observe>(action::check_file_exists())
.help("Input file path");
break;
arg_parser.add_positional_argument("output").help("Output file path");
break;
arg_parser.add_optional_argument<none_type>("help", "h")
.action<action_type::on_flag>(action::print_help(arg_parser, EXIT_SUCCESS))
.help("Display the help message");
break;
arg_parser.add_optional_argument<none_type>("version", "v")
.action<action_type::on_flag>([&arg_parser]() {
arg_parser.print_version();
std::exit(EXIT_SUCCESS);
})
.help("Dsiplay program version info");
break;
arg_parser.add_optional_argument("input", "i")
.nargs(1ull)
.action<action_type::observe>(action::check_file_exists())
.help("Input file path");
break;
arg_parser.add_optional_argument("output", "o").nargs(1ull).help("Output file path");
break;
arg_parser.add_optional_argument("input", "i")
.nargs(ap::nargs::at_least(1ull))
.action<action_type::observe>(action::check_file_exists())
.help("Input files paths");
break;
arg_parser.add_optional_argument("output", "o")
.nargs(ap::nargs::at_least(1ull))
.help("Output files paths");
break;
}
}
} // namespace detail
} // namespace ap
static std::unique_ptr< argument_group > create(argument_parser &parser, std::string_view name)
Factory method to create an argument group.
std::vector< arg_ptr_t > arg_ptr_vec_t
std::string _program_name
The name of the program in the format "<parent-parser-names>... <program-name>".
argument_parser & program_version(const version &version) noexcept
Set the program version.
optional_argument< T > & add_optional_argument(const std::string_view name, const detail::argument_name_discriminator name_discr=n_primary)
Adds an optional argument to the parser's configuration.
std::vector< std::string > try_parse_known_args(int argc, char *argv[])
Parses the known command-line arguments and exits on error.
void _verify_group_requirements(const argument_group &group, const bool suppress_group_checks, const bool suppress_arg_checks) const
Verifies whether the requirements of the given argument group are satisfied.
void _validate_group(const argument_group &group)
Check if the given group belongs to the parser.
std::string_view program_name() const noexcept
void _parse_flag_token(const detail::argument_token &tok, parsing_state &state)
Parse a single command-line argument flag token.
bool finalized() const noexcept
Check whether the parser has finalized parsing its own arguments.
static constexpr std::uint8_t _indent_width
~argument_parser()=default
std::shared_ptr< detail::argument_base > arg_ptr_t
arg_group_ptr_vec_t _argument_groups
The list of argument groups.
arg_ptr_vec_t _optional_args
The list of optional arguments.
arg_token_vec_t _tokenize(AIt args_begin, const AIt args_end, const parsing_state &state)
Converts the command-line arguments into a list of tokens.
std::optional< std::string > _program_description
The description of the program.
bool _validate_flag_token(detail::argument_token &tok) noexcept
Check if a flag token is valid based on its value.
std::pair< bool, bool > _are_checks_suppressed() const noexcept
Check whether required argument group checks or argument checks suppressing is enabled.
void print_help(const bool verbose, std::ostream &os=std::cout) const noexcept
Prints the argument parser's help message to an output stream.
optional_argument< 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...
argument_parser(const argument_parser &)=delete
void _verify_arg_name_pattern(const std::string_view arg_name) const
Verifies the pattern of an argument name and if it's invalid, an error is thrown.
void _parse_token(const detail::argument_token &tok, parsing_state &state)
Parse a single command-line argument token.
std::string _name
The name of the parser.
std::string_view name() const noexcept
Returns the parser's name.
void _verify_argument_requirements(const arg_ptr_t &arg, const bool suppress_arg_checks) const
Verifies whether the requirements of the given argument are satisfied.
bool _is_arg_name_used(const detail::argument_name &arg_name, const detail::argument_name::match_type m_type=detail::argument_name::m_any) const noexcept
Check if an argument name is already used.
std::unique_ptr< argument_group > arg_group_ptr_t
arg_ptr_vec_t _positional_args
The list of positional arguments.
arg_parser_ptr_vec_t _subparsers
The list of subparsers.
argument_parser & program_description(std::string_view description) noexcept
Set the program description.
T value(std::string_view arg_name) const
Get the value of the given argument.
bool _invoked
A flag indicating whether the parser has been invoked to parse arguments.
std::string_view _strip_flag_prefix(const detail::argument_token &tok) const noexcept
Removes the flag prefix from a flag token's value.
static constexpr char _flag_prefix_char
void _validate_argument_configuration() const
Validate whether the definition/configuration of the parser's arguments is correct.
void _parse_args_impl(AIt args_begin, const AIt args_end, parsing_state &state)
Implementation of parsing command-line arguments.
T value_or(std::string_view arg_name, U &&fallback_value) const
Get the value of the given argument, if it has any, or a fallback value, if not.
void _verify_final_state() const
Verifies the correctness of the parsed command-line arguments.
bool has_value(std::string_view arg_name) const noexcept
Check if the given argument has a value.
bool _verbose
Verbosity flag.
argument_group & _gr_optional_args
The optional argument group.
arg_ptr_t _get_argument(std::string_view arg_name) const noexcept
Get the argument with the specified name.
argument_parser & resolved_parser() noexcept
Returns the deepest invoked parser.
argument_group & add_group(const std::string_view name) noexcept
Adds an argument group with the given name to the parser's configuration.
argument_parser & operator=(const argument_parser &)=delete
std::vector< T > values(std::string_view arg_name) const
Get all values of the given argument.
void _parse_value_token(const detail::argument_token &tok, parsing_state &state)
Parse a single command-line argument value token.
void _set_argument_value(const std::string_view value, parsing_state &state) noexcept
Set the value for the currently processed argument.
argument_parser & default_arguments(const AR &arg_discriminators) noexcept
Add default arguments to the argument parser.
bool invoked() const noexcept
Check whether this parser was invoked.
void _print_group(std::ostream &os, const argument_group &group, const bool verbose) const noexcept
Print the given argument list to an output stream.
static constexpr std::uint8_t _primary_flag_prefix_length
std::unique_ptr< argument_parser > arg_parser_ptr_t
std::vector< arg_group_ptr_t > arg_group_ptr_vec_t
std::vector< std::string > parse_known_args(int argc, char *argv[])
Parses the known command-line arguments.
argument_parser & verbose(const bool v=true) noexcept
Set the verbosity mode.
static constexpr std::string_view _flag_prefix
typename arg_ptr_vec_t::iterator arg_ptr_vec_iter_t
auto _name_match_predicate(const std::string_view arg_name, const detail::argument_name::match_type m_type=detail::argument_name::m_any) const noexcept
Returns a unary predicate function which checks if the given name matches the argument's name.
std::vector< arg_parser_ptr_t > arg_parser_ptr_vec_t
unknown_policy _unknown_policy
Policy for unknown arguments.
void try_parse_args(int argc, char *argv[])
Parses the command-line arguments and exits on error.
positional_argument< T > & add_positional_argument(const std::string_view name)
Adds a positional argument to the parser's configuration.
void print_version(std::ostream &os=std::cout) const noexcept
Prints the argument parser's version info to an output stream.
std::vector< detail::argument_token > arg_token_vec_t
arg_ptr_vec_iter_t _find_opt_arg(const detail::argument_token &flag_tok) noexcept
Find an optional argument based on a flag token.
argument_parser & add_subparser(const std::string_view name)
Adds an subparser with the given name to the parser's configuration.
typename arg_token_vec_t::const_iterator arg_token_vec_iter_t
bool is_used(std::string_view arg_name) const noexcept
Check if a specific argument was used in the command-line.
bool _validate_compound_flag_token(detail::argument_token &tok) noexcept
Check if a flag token is a valid compound argument flag based on its value.
argument_parser & unknown_arguments_policy(const unknown_policy policy) noexcept
Set the unknown argument flags handling policy.
std::size_t count(std::string_view arg_name) const noexcept
Get the given argument's usage count.
void _print_subparsers(std::ostream &os) const noexcept
detail::argument_token::token_type _deduce_token_type(const std::string_view arg_value) const noexcept
Returns the most appropriate initial token type based on a command-line argument's value.
bool _finalized
A flag indicating whether the parsing process has been finalized.
static constexpr std::uint8_t _secondary_flag_prefix_length
void _tokenize_arg(const std::string_view arg_value, arg_token_vec_t &toks, const parsing_state &state)
Appends an argument token(s) created from arg_value to the toks vector.
friend std::ostream & operator<<(std::ostream &os, const argument_parser &parser) noexcept
Prints the argument parser's details to an output stream.
std::optional< std::string > _program_version
The version of the program.
void parse_args(int argc, char *argv[])
Parses the command-line arguments.
argument_group & _gr_positional_args
The positional argument group.
std::string join(const R &range, const std::string_view delimiter=", ")
Joins elements of a range into a single string with a delimiter.
Definition string.hpp:49
ap::action_type::on_flag::type print_help(const argument_parser &parser, const std::optional< int > exit_code=std::nullopt, std::ostream &os=std::cout) noexcept
Returns an on-flag action which prints the argument parser's help message.
util::callable_type< ap::action_type::observe, std::string > check_file_exists() noexcept
Returns an observe action which checks whether lower_bound file with the given name exists.
argument_name_discriminator
Argument name member discriminator.
@ n_primary
Represents the primary name (used with a long flag prefix –).
void add_default_argument(const default_argument, argument_parser &) noexcept
Adds a predefined/default positional argument to the parser.
constexpr range at_least(const count_type n) noexcept
range class builder function. Creates a range [n, inf).
Definition range.hpp:128
bool contains_whitespaces(std::string_view str) noexcept
Checks whether a string contains any whitespace characters.
Definition string.hpp:34
unknown_policy
The enumeration of policies for handling unknown arguments.
@ warn
Issue a warning when an unknown argument is encountered.
@ as_values
Treat unknown arguments as positional values.
@ ignore
Ignore unknown arguments.
@ fail
Throw an exception when an unknown argument is encountered.
default_argument
The enumeration of default arguments provided by the library.
@ p_output
A positional argument representing a single output file path. Equivalent to:
@ o_help
An optional argument representing the program's help flag. Equivalent to:
@ p_input
A positional argument representing a single input file path. Equivalent to:
@ o_input
A positional argument representing multiple input file paths. Equivalent to:
@ o_multi_input
A positional argument representing multiple input file paths. Equivalent to:
@ o_version
An optional argument representing the program's version flag. Equivalent to:
@ o_output
A positional argument representing multiple output file paths. Equivalent to:
@ o_multi_output
A positional argument representing multiple output file paths. Equivalent to:
void set_parser(argument_parser &parser)
Update the parser-specific parameters of the state object.
parsing_state(argument_parser &parser, const bool parse_known_only=false)
std::vector< std::string > unknown_args
A vector of unknown argument values.
const bool parse_known_only
A flag indicating whether only known arguments should be parsed.
arg_ptr_vec_iter_t curr_pos_arg_it
An iterator pointing to the next positional argument to be processed.
arg_ptr_t curr_arg
The currently processed argument.
Base type for the argument parser functionality errors/exceptions.
match_type
Specifies the type of argument name match.
@ m_any
Matches either the primary or the secondary name.
@ m_primary
Matches only the primary name.
@ m_secondary
Matches only the secondary name.
token_type
The token type discriminator.
@ t_flag_compound
Represents a compound flag argument (secondary flag matching multiple arguments).
@ t_value
Represents a value argument.
@ t_flag_secondary
Represents a secondary (-) flag argument.
@ t_flag_primary
Represents a primary (–) flag argument.
static invalid_configuration argument_name_used(const detail::argument_name &arg_name) noexcept
static invalid_configuration invalid_argument_name(const std::string_view arg_name, const std::string_view reason) noexcept
static lookup_failure argument_not_found(const std::string_view &arg_name) noexcept
static parsing_failure invalid_nvalues(const detail::argument_name &arg_name, const std::weak_ordering ordering) noexcept
static parsing_failure unknown_argument(const std::string_view arg_name) noexcept