From b89b854f050d9f1cd0f8196b3eb62b9858b62162 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 25 Feb 2025 22:27:27 +0100 Subject: [PATCH 1/8] Change consuming args --- include/argparse.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index b2976d9c..da711eba 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -855,7 +855,7 @@ namespace argparse { throw parsing_error(std::format("argument {}: expected one argument", join(get_names(), "/"))); } - consume_arg(args, it, m_value); + m_value = consume_arg(args, it); } break; case store_true: @@ -974,7 +974,7 @@ namespace argparse } else { - consume_arg(args, it, m_value); + m_value = consume_arg(args, it); } break; } @@ -999,8 +999,7 @@ namespace argparse auto parse_arguments_number(tokens & args, tokens::iterator it, std::size_t args_number) -> void { - auto values = std::vector(args_number); - consume_args(args, it, values); + auto values = consume_args(args, it, args_number); m_value = m_options.type_handler->transform(values); } @@ -1091,8 +1090,9 @@ namespace argparse return result; } - auto consume_arg(tokens & args, tokens::iterator & arg_it, std::any & value) const -> void + auto consume_arg(tokens & args, tokens::iterator & arg_it) const -> std::any { + std::any value; if (!m_options.type_handler->from_string(arg_it->m_token, value)) { throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg_it->m_token)); @@ -1102,14 +1102,17 @@ namespace argparse check_choices(value); } arg_it = args.erase(arg_it); + return value; } - auto consume_args(tokens & args, tokens::iterator & arg_it, std::vector & values) const -> void + auto consume_args(tokens & args, tokens::iterator & arg_it, std::size_t number) const -> std::vector { - for (auto & value : values) + std::vector values; + for (std::size_t i = 0; i < number; ++i) { - consume_arg(args, arg_it, value); + values.push_back(consume_arg(args, arg_it)); } + return values; } auto get_name_for_dest() const -> std::string From d3141a4acf8703b60847558a3f9f4e6adae0391f Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 25 Feb 2025 22:52:34 +0100 Subject: [PATCH 2/8] Change consuming args --- include/argparse.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index da711eba..307e760c 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -855,7 +855,7 @@ namespace argparse { throw parsing_error(std::format("argument {}: expected one argument", join(get_names(), "/"))); } - m_value = consume_arg(args, it); + m_value = consume_arg(*it); } break; case store_true: @@ -959,7 +959,7 @@ namespace argparse { throw parsing_error(std::format("argument {}: expected {} argument{}", join(get_names(), "/"), std::to_string(nargs_number), nargs_number > 1 ? "s" : "")); } - parse_arguments_number(args, it, nargs_number); + parse_arguments_number(it, nargs_number); } auto parse_arguments_option(tokens & args, tokens::iterator it) -> void @@ -974,14 +974,14 @@ namespace argparse } else { - m_value = consume_arg(args, it); + m_value = consume_arg(*it); } break; } case zero_or_more: { auto const args_number = count_args(it, args.end()); - parse_arguments_number(args, it, args_number); + parse_arguments_number(it, args_number); break; } case one_or_more: @@ -991,15 +991,15 @@ namespace argparse { throw parsing_error(std::format("argument {}: expected at least one argument", join(get_names(), "/"))); } - parse_arguments_number(args, it, args_number); + parse_arguments_number(it, args_number); break; } } } - auto parse_arguments_number(tokens & args, tokens::iterator it, std::size_t args_number) -> void + auto parse_arguments_number(tokens::iterator it, std::size_t args_number) -> void { - auto values = consume_args(args, it, args_number); + auto values = consume_args(it, args_number); m_value = m_options.type_handler->transform(values); } @@ -1090,27 +1090,27 @@ namespace argparse return result; } - auto consume_arg(tokens & args, tokens::iterator & arg_it) const -> std::any + auto consume_arg(Token & arg) const -> std::any { std::any value; - if (!m_options.type_handler->from_string(arg_it->m_token, value)) + if (!m_options.type_handler->from_string(arg.m_token, value)) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg_it->m_token)); + throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg.m_token)); } if (!m_options.choices.empty()) { check_choices(value); } - arg_it = args.erase(arg_it); + arg.m_consumed = true; return value; } - auto consume_args(tokens & args, tokens::iterator & arg_it, std::size_t number) const -> std::vector + auto consume_args(tokens::iterator arg_it, std::size_t number) const -> std::vector { std::vector values; for (std::size_t i = 0; i < number; ++i) { - values.push_back(consume_arg(args, arg_it)); + values.push_back(consume_arg(*(arg_it + i))); } return values; } From 95bdccafb7cf05187ae102a0ba07d26fa92e6825 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 25 Feb 2025 23:31:01 +0100 Subject: [PATCH 3/8] Refactor: construct vector directly --- include/argparse.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 307e760c..3c7401ff 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -809,9 +809,8 @@ namespace argparse auto consume_args(std::ranges::view auto args) const -> std::vector { - std::vector values; - std::ranges::copy(std::views::transform(args, [this](auto & arg) { return consume_arg(arg); }), std::back_inserter(values)); - return values; + auto transformation = std::views::transform(args, [this](auto & arg) { return consume_arg(arg); }) | std::views::common; + return std::vector(transformation.begin(), transformation.end()); } private: From 216043f116736ca2d4287e3cf2b3a212ff1d3086 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 25 Feb 2025 23:32:10 +0100 Subject: [PATCH 4/8] Cleanup: use CTAD --- include/argparse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/argparse.h b/include/argparse.h index 3c7401ff..d6665ee3 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -810,7 +810,7 @@ namespace argparse auto consume_args(std::ranges::view auto args) const -> std::vector { auto transformation = std::views::transform(args, [this](auto & arg) { return consume_arg(arg); }) | std::views::common; - return std::vector(transformation.begin(), transformation.end()); + return std::vector(transformation.begin(), transformation.end()); } private: From 898810cfaedfa655db08232c6cb5cc449f65e481 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 4 Mar 2025 22:26:03 +0100 Subject: [PATCH 5/8] Cleanup: use post-increment --- include/argparse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/argparse.h b/include/argparse.h index d6665ee3..241da269 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -1109,7 +1109,7 @@ namespace argparse std::vector values; for (std::size_t i = 0; i < number; ++i) { - values.push_back(consume_arg(*(arg_it + i))); + values.push_back(consume_arg(*arg_it++)); } return values; } From 4bc25f123a9f6e4d99eb35cb9a9a6d0739d9fbee Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 4 Mar 2025 22:52:25 +0100 Subject: [PATCH 6/8] Change passing and consuming args --- include/argparse.h | 166 +++++++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 66 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 241da269..ac9e4c92 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -829,53 +829,70 @@ namespace argparse auto parse_args(tokens args) -> tokens override { - auto const pseudo_it = find_pseudo_arg(args); - if (auto [it, name] = find_arg(args.begin(), pseudo_it); it != pseudo_it) + auto consumable = std::ranges::drop_while_view(args, [](auto const & token) { return token.m_consumed; }) + | std::views::take_while([](auto const & token) { return token.m_token != "--"; }) + | std::views::common; + for (auto it = consumable.begin(); it != consumable.end(); ++it) { - it = consume_name(args, it, name); - - switch (m_options.action) + if (auto [found, name] = has_arg(it); found) { - case store: - if (has_nargs()) - { - if (has_nargs_number()) + auto const value = consume_name(it, name); + + switch (m_options.action) + { + case store: + if (has_nargs()) { - parse_arguments_number(args, it); + if (has_nargs_number()) + { + parse_arguments_number(consumable, std::next(it)); + } + else + { + parse_arguments_option(consumable, std::next(it)); + } } else { - parse_arguments_option(args, it); + if (value.empty()) + { + if (auto nit = std::next(it); (nit == consumable.end()) || nit->m_token.starts_with("-")) + { + throw parsing_error(std::format("argument {}: expected one argument", join(get_names(), "/"))); + } + else + { + m_value = consume_arg(*nit); + } + } + else + { + m_value = consume_arg(value); + } } - } - else - { - if (it == args.end() || it->m_token.starts_with("-")) - { - throw parsing_error(std::format("argument {}: expected one argument", join(get_names(), "/"))); - } - m_value = consume_arg(*it); - } - break; - case store_true: - m_value = true; - break; - case store_false: - m_value = false; - break; - case store_const: - m_value = m_options.const_; - break; - case help: - m_value = true; - throw HelpRequested(); - case version: - m_value = true; - throw VersionRequested(); + break; + case store_true: + m_value = true; + break; + case store_false: + m_value = false; + break; + case store_const: + m_value = m_options.const_; + break; + case help: + m_value = true; + throw HelpRequested(); + case version: + m_value = true; + throw VersionRequested(); + } + + m_present = true; } - m_present = true; } - else + + if (!m_present) { switch (m_options.action) { @@ -950,7 +967,7 @@ namespace argparse } private: - auto parse_arguments_number(tokens & args, tokens::iterator it) -> void + auto parse_arguments_number(std::ranges::view auto args, auto it) -> void { auto const nargs_number = get_nargs_number(); auto const args_number = count_args(it, args.end()); @@ -961,7 +978,7 @@ namespace argparse parse_arguments_number(it, nargs_number); } - auto parse_arguments_option(tokens & args, tokens::iterator it) -> void + auto parse_arguments_option(std::ranges::view auto args, auto it) -> void { switch (get_nargs_option()) { @@ -996,7 +1013,7 @@ namespace argparse } } - auto parse_arguments_number(tokens::iterator it, std::size_t args_number) -> void + auto parse_arguments_number(auto it, std::size_t args_number) -> void { auto values = consume_args(it, args_number); m_value = m_options.type_handler->transform(values); @@ -1007,45 +1024,44 @@ namespace argparse return std::ranges::find(args, Token{"--"}); } - auto find_arg(tokens::iterator begin, tokens::iterator end) const -> std::pair + auto has_arg(auto it) const -> std::pair { - for (auto it = begin; it != end; ++it) + for (auto const & name: m_options.names) { - for (auto const & name : m_options.names) + if (name[1] != '-') { - if (name[1] != '-') + if (it->m_token.starts_with("-") && !it->m_token.starts_with("--") && it->m_token.find(name[1]) != std::string::npos) { - if (it->m_token.starts_with("-") && !it->m_token.starts_with("--") && it->m_token.find(name[1]) != std::string::npos) - { - return {it, name}; - } + return {true, name}; } - else + } + else + { + auto const [first_it, second_it] = std::ranges::mismatch(name, it->m_token); + if (first_it == name.end() && (second_it == it->m_token.end() || *second_it == '=')) { - auto const [first_it, second_it] = std::ranges::mismatch(name, it->m_token); - if (first_it == name.end() && (second_it == it->m_token.end() || *second_it == '=')) - { - return {it, name}; - } + return {true, name}; } } } - return {end, ""}; + return {false, ""}; } - auto consume_name(tokens & args, tokens::iterator it, std::string const & name) const -> tokens::iterator + auto consume_name(auto it, std::string const & name) const -> std::string { if (auto const & arg = *it; arg.m_token.starts_with("--")) { if (auto const pos = arg.m_token.find('='); pos != std::string::npos) { auto const value = arg.m_token.substr(pos + 1); - *it = Token{value}; + it->m_consumed = true; + return value; } else { - it = args.erase(it); + it->m_consumed = true; + return ""; } } else @@ -1058,27 +1074,31 @@ namespace argparse { if (pos == 1) { - it->m_token.erase(0, 1); + it->m_consumed = true; + return it->m_token.substr(pos); } else { auto const prefix = it->m_token.substr(0, pos); auto const value = it->m_token.substr(pos); *it = Token{prefix}; - it = args.insert(it, Token{value}); + return value; } } + else + { + return ""; + } } else { - it = args.erase(it); + it->m_consumed = true; + return ""; } } - - return it; } - auto count_args(tokens::const_iterator it, tokens::const_iterator end) const -> std::size_t + auto count_args(auto it, auto end) const -> std::size_t { auto result = std::size_t(0); while (it != end && !it->m_token.starts_with('-')) @@ -1104,7 +1124,21 @@ namespace argparse return value; } - auto consume_args(tokens::iterator arg_it, std::size_t number) const -> std::vector + auto consume_arg(std::string const & arg) -> std::any + { + std::any value; + if (!m_options.type_handler->from_string(arg, value)) + { + throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg)); + } + if (!m_options.choices.empty()) + { + check_choices(value); + } + return value; + } + + auto consume_args(auto arg_it, std::size_t number) const -> std::vector { std::vector values; for (std::size_t i = 0; i < number; ++i) From ef6091825b3d36b1a5b4053cbb440cb48fb811ed Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 4 Mar 2025 23:00:05 +0100 Subject: [PATCH 7/8] Cleanup: add const qualifier --- include/argparse.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/argparse.h b/include/argparse.h index ac9e4c92..719af5a1 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -1124,7 +1124,7 @@ namespace argparse return value; } - auto consume_arg(std::string const & arg) -> std::any + auto consume_arg(std::string const & arg) const -> std::any { std::any value; if (!m_options.type_handler->from_string(arg, value)) From 824dd8b0b387b97239e0e9e07b46b2792f147347 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 4 Mar 2025 23:00:23 +0100 Subject: [PATCH 8/8] Cleanup: reduce code duplication --- include/argparse.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/include/argparse.h b/include/argparse.h index 719af5a1..bcc48ec0 100644 --- a/include/argparse.h +++ b/include/argparse.h @@ -1111,17 +1111,8 @@ namespace argparse auto consume_arg(Token & arg) const -> std::any { - std::any value; - if (!m_options.type_handler->from_string(arg.m_token, value)) - { - throw parsing_error(std::format("argument {}: invalid value: '{}'", join(get_names(), "/"), arg.m_token)); - } - if (!m_options.choices.empty()) - { - check_choices(value); - } arg.m_consumed = true; - return value; + return consume_arg(arg.m_token); } auto consume_arg(std::string const & arg) const -> std::any