From b9799ea79235841448a984a77683856ecd6d4e48 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 11 Nov 2025 12:11:48 +0100 Subject: [PATCH 1/3] Refactor: pass functor instead of storing it --- include/argparse.hpp | 104 +++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index e967ec5..a82fa67 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -655,9 +655,8 @@ namespace argparse class ArgumentImpl { public: - ArgumentImpl(Options options, std::function name_for_error) + ArgumentImpl(Options options) : m_options(std::move(options)) - , m_name_for_error(name_for_error) { } @@ -761,36 +760,36 @@ namespace argparse return join(m_options.choices | std::views::transform([&](auto const & choice) { return m_options.type_handler->to_string(choice); }), separator); } - auto parse_arguments(std::ranges::view auto tokens) const -> std::any + auto parse_arguments(std::ranges::view auto tokens, std::function name_for_error) const -> std::any { - auto const values = consume_tokens(tokens); + auto const values = consume_tokens(tokens, name_for_error); return m_options.type_handler->transform(values); } - auto consume_token(Token & token) const -> std::any + auto consume_token(Token & token, std::function name_for_error) const -> std::any { token.m_consumed = true; - return process_token(token.m_token); + return process_token(token.m_token, name_for_error); } - auto process_token(std::string const & token) const -> std::any + auto process_token(std::string const & token, std::function name_for_error) const -> std::any { auto const value = m_options.type_handler->from_string(token); if (!value.has_value()) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", m_name_for_error(), token)); + throw parsing_error(std::format("argument {}: invalid value: '{}'", name_for_error(), token)); } check_choices(value); return value; } - auto consume_tokens(std::ranges::view auto tokens) const -> std::vector + auto consume_tokens(std::ranges::view auto tokens, std::function name_for_error) const -> std::vector { auto result = std::vector(); auto consumed = std::vector(); for (auto & token : tokens) { - result.push_back(process_token(token.m_token)); + result.push_back(process_token(token.m_token, name_for_error)); consumed.push_back(&token); } std::ranges::for_each(consumed, [](auto token) { token->m_consumed = true; }); @@ -834,14 +833,13 @@ namespace argparse private: Options m_options; - std::function m_name_for_error; }; class ArgumentBase : public Argument, public Formattable { public: explicit ArgumentBase(Options options) - : m_impl(std::move(options), [this]() { return this->get_name_for_error(); }) + : m_impl(std::move(options)) { } virtual ~ArgumentBase() = default; @@ -946,24 +944,24 @@ namespace argparse return m_impl.get_action(); } - auto parse_arguments(std::ranges::view auto tokens) const -> std::any + auto parse_arguments(std::ranges::view auto tokens, std::function name_for_error) const -> std::any { - return m_impl.parse_arguments(tokens); + return m_impl.parse_arguments(tokens, name_for_error); } - auto consume_token(Token & token) const -> std::any + auto consume_token(Token & token, std::function name_for_error) const -> std::any { - return m_impl.consume_token(token); + return m_impl.consume_token(token, name_for_error); } - auto process_token(std::string const & token) const -> std::any + auto process_token(std::string const & token, std::function name_for_error) const -> std::any { - return m_impl.process_token(token); + return m_impl.process_token(token, name_for_error); } - auto consume_tokens(std::ranges::view auto tokens) const -> std::vector + auto consume_tokens(std::ranges::view auto tokens, std::function name_for_error) const -> std::vector { - return m_impl.consume_tokens(tokens); + return m_impl.consume_tokens(tokens, name_for_error); } auto check_choices(std::any const & value) const -> void @@ -986,8 +984,6 @@ namespace argparse m_impl.append_value(value, values); } - virtual auto get_name_for_error() const -> std::string = 0; - static auto is_negative_number(std::string const & token) -> bool { if (auto const parsed = from_string(token); parsed.has_value()) @@ -1004,28 +1000,28 @@ namespace argparse class StoreAction { public: - auto perform(ArgumentBase const & base, std::any & value, std::string const & val, std::ranges::view auto tokens) const -> void + auto perform(ArgumentBase const & base, std::any & value, std::function name_for_error, std::string const & val, std::ranges::view auto tokens) const -> void { if (base.has_nargs()) { if (base.has_nargs_number()) { - parse_arguments_number(base, value, tokens); + parse_arguments_number(base, value, name_for_error, tokens); } else { - parse_arguments_option(base, value, tokens); + parse_arguments_option(base, value, name_for_error, tokens); } } else { if (val.empty()) { - value = base.consume_token(tokens.front()); + value = base.consume_token(tokens.front(), name_for_error); } else { - value = base.process_token(val); + value = base.process_token(val, name_for_error); } } } @@ -1044,10 +1040,10 @@ namespace argparse } private: - auto parse_arguments_number(ArgumentBase const & base, std::any & value, std::ranges::view auto tokens) const -> void + auto parse_arguments_number(ArgumentBase const & base, std::any & value, std::function name_for_error, std::ranges::view auto tokens) const -> void { auto const nargs_number = base.get_nargs_number(); - auto const values = base.consume_tokens(tokens | std::views::take(nargs_number)); + auto const values = base.consume_tokens(tokens | std::views::take(nargs_number), name_for_error); if (values.size() < nargs_number) { throw parsing_error(std::format("argument {}: expected {} argument{}", base.get_joined_names(), std::to_string(nargs_number), nargs_number > 1 ? "s" : "")); @@ -1055,7 +1051,7 @@ namespace argparse value = base.get_transformed(values); } - auto parse_arguments_option(ArgumentBase const & base, std::any & value, std::ranges::view auto tokens) const -> void + auto parse_arguments_option(ArgumentBase const & base, std::any & value, std::function name_for_error, std::ranges::view auto tokens) const -> void { switch (base.get_nargs_option()) { @@ -1063,7 +1059,7 @@ namespace argparse { if (!tokens.empty()) { - value = base.consume_token(tokens.front()); + value = base.consume_token(tokens.front(), name_for_error); } else { @@ -1073,12 +1069,12 @@ namespace argparse } case zero_or_more: { - value = base.parse_arguments(tokens); + value = base.parse_arguments(tokens, name_for_error); break; } case one_or_more: { - if (auto const values = base.consume_tokens(tokens); !values.empty()) + if (auto const values = base.consume_tokens(tokens, name_for_error); !values.empty()) { value = base.get_transformed(values); } @@ -1095,7 +1091,7 @@ namespace argparse class StoreConstAction { public: - auto perform(ArgumentBase const & base, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & base, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { value = base.get_const(); } @@ -1117,7 +1113,7 @@ namespace argparse class StoreTrueAction { public: - auto perform(ArgumentBase const & /* base */, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & /* base */, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { value = true; } @@ -1139,7 +1135,7 @@ namespace argparse class StoreFalseAction { public: - auto perform(ArgumentBase const & /* base */, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & /* base */, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { value = false; } @@ -1161,7 +1157,7 @@ namespace argparse class HelpAction { public: - auto perform(ArgumentBase const & /* base */, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & /* base */, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { value = true; throw HelpRequested(); @@ -1180,7 +1176,7 @@ namespace argparse class VersionAction { public: - auto perform(ArgumentBase const & /* base */, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & /* base */, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { value = true; throw VersionRequested(); @@ -1199,7 +1195,7 @@ namespace argparse class CountAction { public: - auto perform(ArgumentBase const & /* base */, std::any & value, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void + auto perform(ArgumentBase const & /* base */, std::any & value, std::function /* name_for_error */, std::string const & /* val */, std::ranges::view auto /* tokens */) const -> void { if (!value.has_value()) { @@ -1228,18 +1224,18 @@ namespace argparse class AppendAction { public: - auto perform(ArgumentBase const & base, std::any & value, std::string const & val, std::ranges::view auto tokens) const -> void + auto perform(ArgumentBase const & base, std::any & value, std::function name_for_error, std::string const & val, std::ranges::view auto tokens) const -> void { if (val.empty()) { if (!value.has_value()) { - auto const values = base.consume_tokens(tokens | std::views::take(1)); + auto const values = base.consume_tokens(tokens | std::views::take(1), name_for_error); value = base.get_transformed(values); } else { - auto const v = base.consume_token(tokens.front()); + auto const v = base.consume_token(tokens.front(), name_for_error); base.append_value(v, value); } } @@ -1247,12 +1243,12 @@ namespace argparse { if (!value.has_value()) { - auto const values = base.consume_tokens(std::views::single(Token{val})); + auto const values = base.consume_tokens(std::views::single(Token{val}), name_for_error); value = base.get_transformed(values); } else { - auto const v = base.process_token(val); + auto const v = base.process_token(val, name_for_error); base.append_value(v, value); } } @@ -1283,7 +1279,7 @@ namespace argparse { if (!tokens.empty()) { - m_value = consume_token(tokens.front()); + m_value = consume_token(tokens.front(), get_name_for_error()); } else { @@ -1293,12 +1289,12 @@ namespace argparse } case zero_or_more: { - m_value = parse_arguments(tokens); + m_value = parse_arguments(tokens, get_name_for_error()); break; } case one_or_more: { - if (auto const values = consume_tokens(tokens); !values.empty()) + if (auto const values = consume_tokens(tokens, get_name_for_error()); !values.empty()) { m_value = get_transformed(values); } @@ -1307,9 +1303,9 @@ namespace argparse } } - auto get_name_for_error() const -> std::string override + auto get_name_for_error() const -> std::function { - return get_dest_name(); + return [&]() { return get_dest_name(); }; } static auto get_consumable(Tokens & tokens) @@ -1360,7 +1356,7 @@ namespace argparse { if (has_nargs_number()) { - m_value = parse_arguments(consumable | std::views::take(get_nargs_number())); + m_value = parse_arguments(consumable | std::views::take(get_nargs_number()), get_name_for_error()); } else { @@ -1371,7 +1367,7 @@ namespace argparse { if (!consumable.empty()) { - m_value = consume_token(consumable.front()); + m_value = consume_token(consumable.front(), get_name_for_error()); } } } @@ -1426,7 +1422,7 @@ namespace argparse private: auto perform_action(std::string const & value, std::ranges::view auto tokens) -> void { - std::visit([&](auto const & action) { action.perform(*this, m_value, value, tokens); }, m_action); + std::visit([&](auto const & action) { action.perform(*this, m_value, get_name_for_error(), value, tokens); }, m_action); } auto has_arg(auto it) const -> std::string_view @@ -1514,9 +1510,9 @@ namespace argparse return get_name().substr(1); } - auto get_name_for_error() const -> std::string override + auto get_name_for_error() const -> std::function { - return get_joined_names(); + return [&]() { return get_joined_names(); }; } auto check_errors(std::string_view value, std::ranges::view auto tokens) const -> void From 89d63b1b3e63b1e02c2756d0184fe49e474daad7 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 11 Nov 2025 12:18:43 +0100 Subject: [PATCH 2/3] Make classes move-constructible and move-assignable --- include/argparse.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/argparse.hpp b/include/argparse.hpp index a82fa67..200c674 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -844,6 +844,10 @@ namespace argparse } virtual ~ArgumentBase() = default; + ArgumentBase(ArgumentBase && other) noexcept = default; + + ArgumentBase & operator=(ArgumentBase && other) noexcept = default; + auto get_name() const -> std::string const & override { return m_impl.get_name(); @@ -1348,6 +1352,10 @@ namespace argparse { } + PositionalArgument(PositionalArgument && other) noexcept = default; + + PositionalArgument & operator=(PositionalArgument && other) noexcept = default; + auto parse_tokens(Tokens & tokens) -> void override { auto consumable = get_consumable(tokens); @@ -1586,6 +1594,10 @@ namespace argparse { } + OptionalArgument(OptionalArgument && other) noexcept = default; + + OptionalArgument & operator=(OptionalArgument && other) noexcept = default; + auto parse_tokens(Tokens & tokens) -> void override { auto consumable = get_consumable(tokens); From f6260abbfa761dd27479e550d543bc30fcdc67fe Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 11 Nov 2025 12:19:16 +0100 Subject: [PATCH 3/3] Store arguments in variant class --- include/argparse.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index 200c674..740f2d7 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -1686,8 +1686,8 @@ namespace argparse } }; - using ArgumentUptr = std::unique_ptr; - using ArgumentUptrs = std::vector; + using ArgumentVariant = std::variant; + using Arguments = std::vector; class Formatter { @@ -1931,7 +1931,7 @@ namespace argparse class MutuallyExclusiveGroup { public: - MutuallyExclusiveGroup(ArgumentUptrs & arguments, OptString & version) + MutuallyExclusiveGroup(Arguments & arguments, OptString & version) : m_arguments(arguments) , m_version(version) { @@ -1944,14 +1944,14 @@ namespace argparse } private: - ArgumentUptrs & m_arguments; + Arguments & m_arguments; OptString & m_version; }; class ArgumentBuilder { public: - ArgumentBuilder(ArgumentUptrs & arguments, OptString & version, std::vector names, MutuallyExclusiveGroup const * group = nullptr) + ArgumentBuilder(Arguments & arguments, OptString & version, std::vector names, MutuallyExclusiveGroup const * group = nullptr) : m_arguments(arguments) , m_version(version) { @@ -1968,11 +1968,11 @@ namespace argparse if (is_positional()) { - m_arguments.push_back(std::make_unique(std::move(m_options))); + m_arguments.push_back(PositionalArgument(std::move(m_options))); } else { - m_arguments.push_back(std::make_unique(std::move(m_options))); + m_arguments.push_back(OptionalArgument(std::move(m_options))); } } @@ -2069,22 +2069,22 @@ namespace argparse } private: - ArgumentUptrs & m_arguments; + Arguments & m_arguments; OptString & m_version; Options m_options; }; - static auto cast_to_argument(ArgumentUptr const & up) -> Argument & + static auto cast_to_argument(ArgumentVariant & av) -> Argument & { - return *up; + return std::visit([](auto & argument) -> Argument & { return argument; } , av); } - static auto cast_to_formattable(ArgumentUptr const & up) -> Formattable const & + static auto cast_to_formattable(ArgumentVariant const & av) -> Formattable const & { - return *up; + return std::visit([](auto & argument) -> Formattable const & { return argument; } , av); } - ArgumentUptrs m_arguments; + Arguments m_arguments; OptString m_prog; OptString m_usage; OptString m_description;