63 this->_program_name = name;
73 this->_program_description = description;
94 template <detail::c_range_of<argument::default_positional> AR>
96 for (
const auto arg_discriminator : arg_discriminator_range)
97 detail::add_default_argument(arg_discriminator, *
this);
107 const std::initializer_list<argument::default_positional> arg_discriminator_list
109 return this->default_positional_arguments<>(arg_discriminator_list);
118 template <detail::c_range_of<argument::default_optional> AR>
120 for (
const auto arg_discriminator : arg_discriminator_range)
121 detail::add_default_argument(arg_discriminator, *
this);
131 const std::initializer_list<argument::default_optional> arg_discriminator_list
133 return this->default_optional_arguments<>(arg_discriminator_list);
145 template <detail::c_argument_value_type T = std::
string>
147 this->_verify_arg_name_pattern(primary_name);
150 if (this->_is_arg_name_used(arg_name))
151 throw invalid_configuration::argument_name_used(arg_name);
167 template <detail::c_argument_value_type T = std::
string>
169 std::string_view primary_name, std::string_view secondary_name
171 this->_verify_arg_name_pattern(primary_name);
172 this->_verify_arg_name_pattern(secondary_name);
175 if (this->_is_arg_name_used(arg_name))
176 throw invalid_configuration::argument_name_used(arg_name);
191 template <detail::c_argument_value_type T = std::
string>
193 this->_verify_arg_name_pattern(primary_name);
196 if (this->_is_arg_name_used(arg_name))
197 throw invalid_configuration::argument_name_used(arg_name);
213 template <detail::c_argument_value_type T = std::
string>
215 std::string_view primary_name, std::string_view secondary_name
217 this->_verify_arg_name_pattern(primary_name);
218 this->_verify_arg_name_pattern(secondary_name);
221 if (this->_is_arg_name_used(arg_name))
222 throw invalid_configuration::argument_name_used(arg_name);
234 template <
bool StoreImplicitly = true>
236 return this->add_optional_argument<bool>(primary_name)
237 .default_value(not StoreImplicitly)
238 .implicit_value(StoreImplicitly)
249 template <
bool StoreImplicitly = true>
251 std::string_view primary_name, std::string_view secondary_name
253 return this->add_optional_argument<bool>(primary_name, secondary_name)
254 .default_value(not StoreImplicitly)
255 .implicit_value(StoreImplicitly)
266 this->
parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
274 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
276 this->_parse_args_impl(this->_tokenize(argv));
278 if (this->_are_required_args_bypassed())
281 this->_verify_required_args();
282 this->_verify_nvalues();
292 this->
try_parse_args(std::span(argv + 1,
static_cast<std::size_t
>(argc - 1)));
305 template <detail::c_range_of<std::
string, detail::type_val
idator::convertible> AR>
311 std::cerr <<
"[ERROR] : " << err.what() << std::endl << *
this << std::endl;
312 std::exit(EXIT_FAILURE);
323 [[deprecated(
"The default help argument now uses the `print_config` on-flag action")]]
325 if (this->value<bool>(
"help")) {
326 std::cout << *
this << std::endl;
327 std::exit(EXIT_SUCCESS);
337 [[nodiscard]]
bool has_value(std::string_view arg_name)
const noexcept {
338 const auto arg_opt = this->_get_argument(arg_name);
339 return arg_opt ? arg_opt->get().has_value() :
false;
346 [[nodiscard]] std::size_t
count(std::string_view arg_name)
const noexcept {
347 const auto arg_opt = this->_get_argument(arg_name);
348 return arg_opt ? arg_opt->get().count() : 0ull;
357 template <detail::c_argument_value_type T = std::
string>
358 [[nodiscard]] T
value(std::string_view arg_name)
const {
359 const auto arg_opt = this->_get_argument(arg_name);
361 throw lookup_failure::argument_not_found(arg_name);
363 const auto& arg_value = arg_opt->get().value();
365 return std::any_cast<T>(arg_value);
367 catch (
const std::bad_any_cast& err) {
368 throw type_error::invalid_value_type(arg_opt->get().name(),
typeid(T));
380 template <detail::c_argument_value_type T = std::
string, std::convertible_to<T> U>
381 [[nodiscard]] T
value_or(std::string_view arg_name, U&& default_value)
const {
382 const auto arg_opt = this->_get_argument(arg_name);
384 throw lookup_failure::argument_not_found(arg_name);
387 const auto& arg_value = arg_opt->get().value();
388 return std::any_cast<T>(arg_value);
390 catch (
const std::logic_error&) {
393 return T{std::forward<U>(default_value)};
395 catch (
const std::bad_any_cast& err) {
396 throw type_error::invalid_value_type(arg_opt->get().name(),
typeid(T));
406 template <detail::c_argument_value_type T = std::
string>
407 [[nodiscard]] std::vector<T>
values(std::string_view arg_name)
const {
408 const auto arg_opt = this->_get_argument(arg_name);
410 throw lookup_failure::argument_not_found(arg_name);
412 const auto& arg = arg_opt->get();
415 if (not arg.has_parsed_values() and arg.has_value())
416 return std::vector<T>{std::any_cast<T>(arg.value())};
421 std::views::transform(
422 arg.values(), [](
const std::any&
value) { return std::any_cast<T>(value); }
424 std::back_inserter(
values)
428 catch (
const std::bad_any_cast& err) {
429 throw type_error::invalid_value_type(arg.name(),
typeid(T));
439 if (this->_program_name)
440 os <<
"Program: " << this->_program_name.value() << std::endl;
442 if (this->_program_description)
444 << std::string(this->_indent_width,
' ') << this->_program_description.value()
447 if (not this->_positional_args.empty()) {
448 os <<
"\nPositional arguments:\n";
449 this->_print(os, this->_positional_args,
verbose);
452 if (not this->_optional_args.empty()) {
453 os <<
"\nOptional arguments:\n";
454 this->_print(os, this->_optional_args,
verbose);
469 parser.print_config(parser._verbose, os);
475 friend struct ::ap_testing::argument_parser_test_fixture;
479 using arg_ptr_t = std::unique_ptr<detail::argument_base>;
480 using arg_ptr_list_t = std::vector<arg_ptr_t>;
481 using arg_opt_t = std::optional<std::reference_wrapper<detail::argument_base>>;
483 using arg_token_list_t = std::vector<detail::argument_token>;
484 using arg_token_list_iterator_t =
typename arg_token_list_t::const_iterator;
490 void _verify_arg_name_pattern(
const std::string_view arg_name)
const {
491 if (arg_name.empty())
492 throw invalid_configuration::invalid_argument_name(
493 arg_name,
"An argument name cannot be empty."
496 if (arg_name.front() == this->_flag_prefix_char)
497 throw invalid_configuration::invalid_argument_name(
500 "An argument name cannot begin with a flag prefix character ({}).",
501 this->_flag_prefix_char
505 if (std::isdigit(arg_name.front()))
506 throw invalid_configuration::invalid_argument_name(
507 arg_name,
"An argument name cannot begin with a digit."
516 [[nodiscard]]
auto _name_match_predicate(
const std::string_view arg_name)
const noexcept {
517 return [arg_name](
const arg_ptr_t& arg) {
return arg->name().match(arg_name); };
525 [[nodiscard]]
auto _name_match_predicate(
const detail::argument_name& arg_name)
const noexcept {
526 return [&arg_name](
const arg_ptr_t& arg) {
return arg->name().match(arg_name); };
534 [[nodiscard]]
bool _is_arg_name_used(
const detail::argument_name& arg_name)
const noexcept {
535 const auto predicate = this->_name_match_predicate(arg_name);
537 if (std::ranges::find_if(this->_positional_args, predicate) != this->_positional_args.end())
540 if (std::ranges::find_if(this->_optional_args, predicate) != this->_optional_args.end())
552 template <detail::c_sized_range_of<std::
string, detail::type_val
idator::convertible> AR>
553 [[nodiscard]] arg_token_list_t _tokenize(
const AR& arg_range)
const noexcept {
554 const auto n_args = std::ranges::size(arg_range);
556 return arg_token_list_t{};
558 arg_token_list_t toks;
559 toks.reserve(n_args);
561 for (
const auto& arg : arg_range) {
562 std::string
value =
static_cast<std::string
>(arg);
563 if (this->_is_flag(
value)) {
564 this->_strip_flag_prefix(
value);
565 toks.emplace_back(detail::argument_token::t_flag, std::move(
value));
568 toks.emplace_back(detail::argument_token::t_value, std::move(
value));
580 [[nodiscard]]
bool _is_flag(
const std::string& arg)
const noexcept {
581 if (arg.starts_with(this->_flag_prefix))
582 return this->_is_arg_name_used({arg.substr(this->_primary_flag_prefix_length)});
584 if (arg.starts_with(this->_flag_prefix_char))
585 return this->_is_arg_name_used({arg.substr(this->_secondary_flag_prefix_length)});
594 void _strip_flag_prefix(std::string& arg_flag)
const noexcept {
595 if (arg_flag.starts_with(this->_flag_prefix))
596 arg_flag.erase(0, this->_primary_flag_prefix_length);
598 arg_flag.erase(0, this->_secondary_flag_prefix_length);
606 void _parse_args_impl(
const arg_token_list_t& arg_tokens) {
607 arg_token_list_iterator_t token_it = arg_tokens.begin();
609 this->_parse_positional_args(token_it, arg_tokens.end());
611 std::vector<std::string_view> dangling_values;
612 this->_parse_optional_args(token_it, arg_tokens.end(), dangling_values);
614 if (not dangling_values.empty())
615 throw parsing_failure::argument_deduction_failure(dangling_values);
623 void _parse_positional_args(
624 arg_token_list_iterator_t& token_it,
const arg_token_list_iterator_t& tokens_end
626 for (
const auto& pos_arg : this->_positional_args) {
627 if (token_it == tokens_end)
630 if (token_it->type == detail::argument_token::t_flag)
633 pos_arg->set_value(token_it->value);
646 void _parse_optional_args(
647 arg_token_list_iterator_t& token_it,
648 const arg_token_list_iterator_t& tokens_end,
649 std::vector<std::string_view>& dangling_values
651 std::optional<std::reference_wrapper<arg_ptr_t>> curr_opt_arg;
653 while (token_it != tokens_end) {
654 switch (token_it->type) {
655 case detail::argument_token::t_flag: {
656 if (not dangling_values.empty())
657 throw parsing_failure::argument_deduction_failure(dangling_values);
659 auto opt_arg_it = std::ranges::find_if(
660 this->_optional_args, this->_name_match_predicate(token_it->value)
663 if (opt_arg_it == this->_optional_args.end())
664 throw parsing_failure::unknown_argument(token_it->value);
666 curr_opt_arg = std::ref(*opt_arg_it);
667 if (not curr_opt_arg->get()->mark_used())
668 curr_opt_arg.reset();
672 case detail::argument_token::t_value: {
673 if (not curr_opt_arg) {
674 dangling_values.emplace_back(token_it->value);
678 if (not curr_opt_arg->get()->set_value(token_it->value))
679 curr_opt_arg.reset();
693 [[nodiscard]]
bool _are_required_args_bypassed() const noexcept {
694 return std::ranges::any_of(this->_optional_args, [](
const arg_ptr_t& arg) {
695 return arg->is_used() and arg->bypass_required_enabled();
703 void _verify_required_args()
const {
704 for (
const auto& arg : this->_positional_args)
705 if (not arg->is_used())
706 throw parsing_failure::required_argument_not_parsed(arg->name());
708 for (
const auto& arg : this->_optional_args)
709 if (arg->is_required() and not arg->
has_value())
710 throw parsing_failure::required_argument_not_parsed(arg->name());
717 void _verify_nvalues()
const {
718 for (
const auto& arg : this->_positional_args)
719 if (const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
720 throw parsing_failure::invalid_nvalues(arg->name(), nv_ord);
722 for (
const auto& arg : this->_optional_args)
723 if (const auto nv_ord = arg->nvalues_ordering(); not std::is_eq(nv_ord))
724 throw parsing_failure::invalid_nvalues(arg->name(), nv_ord);
732 arg_opt_t _get_argument(std::string_view arg_name)
const noexcept {
733 const auto predicate = this->_name_match_predicate(arg_name);
735 if (
auto pos_arg_it = std::ranges::find_if(this->_positional_args, predicate);
736 pos_arg_it != this->_positional_args.end()) {
737 return std::ref(**pos_arg_it);
740 if (
auto opt_arg_it = std::ranges::find_if(this->_optional_args, predicate);
741 opt_arg_it != this->_optional_args.end()) {
742 return std::ref(**opt_arg_it);
753 void _print(std::ostream& os,
const arg_ptr_list_t& args,
const bool verbose)
const noexcept {
755 for (
const auto& arg : args)
757 << arg->desc(
verbose, this->_flag_prefix_char).get(this->_indent_width) <<
'\n';
760 std::vector<detail::argument_descriptor> descriptors;
761 descriptors.reserve(args.size());
763 for (
const auto& arg : args)
764 descriptors.emplace_back(arg->desc(
verbose, this->_flag_prefix_char));
766 std::size_t max_arg_name_length = 0ull;
767 for (
const auto& desc : descriptors)
768 max_arg_name_length = std::max(max_arg_name_length, desc.name.length());
770 for (
const auto& desc : descriptors)
771 os <<
'\n' << desc.get_basic(this->_indent_width, max_arg_name_length);
777 std::optional<std::string> _program_name;
778 std::optional<std::string> _program_description;
779 bool _verbose =
false;
781 arg_ptr_list_t _positional_args;
782 arg_ptr_list_t _optional_args;
784 static constexpr uint8_t _primary_flag_prefix_length = 2u;
785 static constexpr uint8_t _secondary_flag_prefix_length = 1u;
786 static constexpr char _flag_prefix_char =
'-';
787 static constexpr std::string _flag_prefix =
"--";
788 static constexpr uint8_t _indent_width = 2;