From c221f63ad45f11dcc349a8a1086e7de3d4d6af93 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Mon, 22 Sep 2025 23:03:34 +0200 Subject: [PATCH 1/8] Add ArgumentCommonImpl class --- include/argparse.hpp | 153 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/include/argparse.hpp b/include/argparse.hpp index b6de356..4db14e2 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -699,6 +699,159 @@ namespace argparse Options m_options; }; + class ArgumentCommonImpl + { + public: + explicit ArgumentCommonImpl(Options options) + : m_options(std::move(options)) + { + } + + auto get_name() const -> std::string const & + { + return m_options.names.front(); + } + + auto get_names() const -> std::vector const & + { + return m_options.names; + } + + auto get_joined_names() const -> std::string + { + return join(m_options.names, "/"); + } + + auto has_nargs() const -> bool + { + return m_options.nargs.has_value(); + } + + auto has_nargs_number() const -> bool + { + return std::holds_alternative(*m_options.nargs); + } + + auto get_nargs_number() const -> std::size_t + { + return std::get(*m_options.nargs); + } + + auto get_nargs_option() const -> Nargs + { + return std::get(*m_options.nargs); + } + + auto is_mutually_exclusive() const -> bool + { + return m_options.mutually_exclusive_group != nullptr; + } + + auto is_mutually_exclusive_with(ArgumentCommonImpl const & other) const -> bool + { + return (m_options.mutually_exclusive_group != nullptr) && (m_options.mutually_exclusive_group == other.m_options.mutually_exclusive_group); + } + + auto expects_argument() const -> bool + { + return m_options.action == store || m_options.action == append; + } + + auto has_version_action() const -> bool + { + return m_options.action == version; + } + + auto get_help() const -> std::string const & + { + return m_options.help; + } + + auto has_choices() const -> bool + { + return !m_options.choices.empty(); + } + + auto get_joined_choices(std::string_view separator) const -> std::string + { + 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) -> std::any + { + auto const values = consume_tokens(tokens); + return m_options.type_handler->transform(values); + } + + auto consume_token(Token & token) const -> std::any + { + token.m_consumed = true; + return process_token(token.m_token); + } + + auto process_token(std::string const & token) 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: '{}'", "name-for-error", token)); + } + check_choices(value); + return value; + } + + auto consume_tokens(std::ranges::view auto tokens) const -> std::vector + { + auto result = std::vector(); + auto consumed = std::vector(); + for (auto & token : tokens) + { + result.push_back(process_token(token.m_token)); + consumed.push_back(&token); + } + std::ranges::for_each(consumed, [](auto token) { token->m_consumed = true; }); + return result; + } + + auto check_choices(std::any const & value) const -> void + { + if (m_options.choices.empty()) + { + return; + } + + if (!std::ranges::any_of( + m_options.choices, + [&](auto const & rhs) { return m_options.type_handler->compare(value, rhs); })) + { + auto const message = std::format( + "argument {}: invalid choice: {} (choose from {})", + get_joined_names(), + m_options.type_handler->to_string(value), + get_joined_choices(", ")); + throw parsing_error(message); + } + } + + auto get_transformed(std::vector const & values) const -> std::any + { + return m_options.type_handler->transform(values); + } + + auto get_size(std::any const & value) const -> std::size_t + { + return m_options.type_handler->size(value); + } + + auto append_value(std::any const & value, std::any & values) const -> void + { + m_options.type_handler->append(value, values); + } + + private: + Options m_options; + }; + class ArgumentCommon : public Argument, public Formattable, public OptionsHolder { public: From 64ff1934d48f541fe620caa0a0b445f42ce2cf62 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Mon, 22 Sep 2025 23:04:02 +0200 Subject: [PATCH 2/8] Add ArgumentCommonBase class --- include/argparse.hpp | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/include/argparse.hpp b/include/argparse.hpp index 4db14e2..fa6f7f6 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -852,6 +852,89 @@ namespace argparse Options m_options; }; + class ArgumentCommonBase : public Argument, public Formattable + { + public: + explicit ArgumentCommonBase(Options options) + : m_impl(std::move(options)) + { + } + virtual ~ArgumentCommonBase() = default; + + auto get_name() const -> std::string const & override + { + return m_impl.get_name(); + } + + auto get_names() const -> std::vector const & override + { + return m_impl.get_names(); + } + + auto is_mutually_exclusive() const -> bool override + { + return m_impl.is_mutually_exclusive(); + } + + auto is_mutually_exclusive_with(Argument const & other) const -> bool override + { + return m_impl.is_mutually_exclusive_with(static_cast(other).m_impl); + } + + auto is_mutually_exclusive_with(Formattable const & other) const -> bool override + { + return m_impl.is_mutually_exclusive_with(static_cast(other).m_impl); + } + + auto expects_argument() const -> bool override + { + return m_impl.expects_argument(); + } + + auto get_joined_names() const -> std::string override + { + m_impl.get_joined_names(); + } + + auto get_help() const -> std::string const & override + { + return m_impl.get_help(); + } + + auto has_nargs() const -> bool override + { + return m_impl.has_nargs(); + } + + auto has_nargs_number() const -> bool override + { + return m_impl.has_nargs_number(); + } + + auto has_choices() const -> bool override + { + return m_impl.has_choices(); + } + + auto get_joined_choices(std::string_view separator) const -> std::string override + { + return m_impl.get_joined_choices(separator); + } + + auto get_nargs_number() const -> std::size_t override + { + return m_impl.get_nargs_number(); + } + + auto get_nargs_option() const -> Nargs override + { + return m_impl.get_nargs_option(); + } + + private: + ArgumentCommonImpl m_impl; + }; + class ArgumentCommon : public Argument, public Formattable, public OptionsHolder { public: From 341bc1d75aac96cae6ec9fa6a834f243ada96022 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 16:47:00 +0200 Subject: [PATCH 3/8] Extend ArgumentCommonImpl class --- include/argparse.hpp | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index fa6f7f6..93ff958 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -702,8 +703,9 @@ namespace argparse class ArgumentCommonImpl { public: - explicit ArgumentCommonImpl(Options options) + ArgumentCommonImpl(Options options, std::function name_for_error) : m_options(std::move(options)) + , m_name_for_error(name_for_error) { } @@ -767,6 +769,36 @@ namespace argparse return m_options.help; } + auto get_default() const -> std::any const & + { + return m_options.default_; + } + + auto get_const() const -> std::any const & + { + return m_options.const_; + } + + auto get_metavar() const -> std::string const & + { + return m_options.metavar; + } + + auto get_dest() const -> std::string const & + { + return m_options.dest; + } + + auto get_required() const -> bool + { + return m_options.required; + } + + auto get_action() const -> Action + { + return m_options.action; + } + auto has_choices() const -> bool { return !m_options.choices.empty(); @@ -794,7 +826,7 @@ namespace argparse auto const value = m_options.type_handler->from_string(token); if (!value.has_value()) { - throw parsing_error(std::format("argument {}: invalid value: '{}'", "name-for-error", token)); + throw parsing_error(std::format("argument {}: invalid value: '{}'", m_name_for_error(), token)); } check_choices(value); return value; @@ -850,6 +882,7 @@ namespace argparse private: Options m_options; + std::function m_name_for_error; }; class ArgumentCommonBase : public Argument, public Formattable From fefcf23d4ccdedc6f9d466870b1dc6664050dd41 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 16:47:49 +0200 Subject: [PATCH 4/8] Extend ArgumentCommonBase class --- include/argparse.hpp | 76 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index 93ff958..e4f3664 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -889,7 +889,7 @@ namespace argparse { public: explicit ArgumentCommonBase(Options options) - : m_impl(std::move(options)) + : m_impl(std::move(options), [this]() { return this->get_name_for_error(); }) { } virtual ~ArgumentCommonBase() = default; @@ -926,7 +926,7 @@ namespace argparse auto get_joined_names() const -> std::string override { - m_impl.get_joined_names(); + return m_impl.get_joined_names(); } auto get_help() const -> std::string const & override @@ -934,6 +934,36 @@ namespace argparse return m_impl.get_help(); } + auto get_default() const -> std::any const & + { + return m_impl.get_default(); + } + + auto get_const() const -> std::any const & + { + return m_impl.get_const(); + } + + auto get_metavar() const -> std::string const & + { + return m_impl.get_metavar(); + } + + auto get_dest() const -> std::string const & + { + return m_impl.get_dest(); + } + + auto get_required() const -> bool + { + return m_impl.get_required(); + } + + auto get_action() const -> Action + { + return m_impl.get_action(); + } + auto has_nargs() const -> bool override { return m_impl.has_nargs(); @@ -964,6 +994,48 @@ namespace argparse return m_impl.get_nargs_option(); } + auto parse_arguments(std::ranges::view auto tokens) -> std::any + { + return m_impl.parse_arguments(tokens); + } + + auto consume_token(Token & token) const -> std::any + { + return m_impl.consume_token(token); + } + + auto process_token(std::string const & token) const -> std::any + { + return m_impl.process_token(token); + } + + auto consume_tokens(std::ranges::view auto tokens) const -> std::vector + { + return m_impl.consume_tokens(tokens); + } + + auto check_choices(std::any const & value) const -> void + { + m_impl.check_choices(value); + } + + auto get_transformed(std::vector const & values) const -> std::any + { + return m_impl.get_transformed(values); + } + + auto get_size(std::any const & value) const -> std::size_t + { + return m_impl.get_size(value); + } + + auto append_value(std::any const & value, std::any & values) const -> void + { + m_impl.append_value(value, values); + } + + virtual auto get_name_for_error() const -> std::string = 0; + private: ArgumentCommonImpl m_impl; }; From c7d1bb4e70af83d4d2ce2e56abd679b7d7742265 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 16:48:34 +0200 Subject: [PATCH 5/8] Use ArgumentCommonBase as base for PositionalArgument and OptionalArgument --- include/argparse.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index e4f3664..47c7961 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -1199,7 +1199,7 @@ namespace argparse } }; - class PositionalArgument final : public ArgumentCommon + class PositionalArgument final : public ArgumentCommonBase { private: auto parse_arguments_option(std::ranges::view auto tokens) -> void @@ -1267,7 +1267,7 @@ namespace argparse public: explicit PositionalArgument(Options options) - : ArgumentCommon(std::move(options)) + : ArgumentCommonBase(std::move(options)) { } @@ -1340,7 +1340,7 @@ namespace argparse std::any m_value; }; - class OptionalArgument final : public ArgumentCommon + class OptionalArgument final : public ArgumentCommonBase { private: auto perform_action(std::string const & value, std::ranges::view auto tokens) -> void @@ -1635,7 +1635,7 @@ namespace argparse public: explicit OptionalArgument(Options options) - : ArgumentCommon(std::move(options)) + : ArgumentCommonBase(std::move(options)) { } @@ -1727,7 +1727,7 @@ namespace argparse } }; - using ArgumentUptr = std::unique_ptr; + using ArgumentUptr = std::unique_ptr; using ArgumentUptrs = std::vector; class Formatter From 6f64e94e226b4ae006d968f9bbda4f09bb201961 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 16:57:53 +0200 Subject: [PATCH 6/8] Remove ArgumentCommon class --- include/argparse.hpp | 159 ------------------------------------------- 1 file changed, 159 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index 47c7961..a8c5518 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -1040,165 +1040,6 @@ namespace argparse ArgumentCommonImpl m_impl; }; - class ArgumentCommon : public Argument, public Formattable, public OptionsHolder - { - public: - explicit ArgumentCommon(Options options) - : OptionsHolder(std::move(options)) - { - } - virtual ~ArgumentCommon() = default; - - auto get_name() const -> std::string const & - { - return get_names().front(); - } - - auto get_names() const -> std::vector const & - { - return OptionsHolder::get_names(); - } - - auto get_joined_names() const -> std::string - { - return join(get_names(), "/"); - } - - auto has_nargs() const -> bool - { - return get_nargs().has_value(); - } - - auto has_nargs_number() const -> bool - { - return std::holds_alternative(*get_nargs()); - } - - auto get_nargs_number() const -> std::size_t - { - return std::get(*get_nargs()); - } - - auto get_nargs_option() const -> Nargs - { - return std::get(*get_nargs()); - } - - auto is_mutually_exclusive() const -> bool - { - return get_mutually_exclusive_group() != nullptr; - } - - auto is_mutually_exclusive_with(Argument const & other) const -> bool - { - return (get_mutually_exclusive_group() != nullptr) && (get_mutually_exclusive_group() == static_cast(other).get_mutually_exclusive_group()); - } - - auto is_mutually_exclusive_with(Formattable const & other) const -> bool - { - return (get_mutually_exclusive_group() != nullptr) && (get_mutually_exclusive_group() == static_cast(other).get_mutually_exclusive_group()); - } - - auto expects_argument() const -> bool - { - return get_action() == store || get_action() == append; - } - - auto has_version_action() const -> bool - { - return get_action() == version; - } - - auto get_help() const -> std::string const & - { - return OptionsHolder::get_help(); - } - - auto has_choices() const -> bool - { - return !get_choices().empty(); - } - - auto get_joined_choices(std::string_view separator) const -> std::string - { - return join(get_choices() | std::views::transform([&](auto const & choice) { return get_type_handler().to_string(choice); }), separator); - } - - protected: - virtual auto get_name_for_error() const -> std::string = 0; - - auto parse_arguments(std::ranges::view auto tokens) -> std::any - { - auto const values = consume_tokens(tokens); - return get_type_handler().transform(values); - } - - auto consume_token(Token & token) const -> std::any - { - token.m_consumed = true; - return process_token(token.m_token); - } - - auto process_token(std::string const & token) const -> std::any - { - auto const value = get_type_handler().from_string(token); - if (!value.has_value()) - { - throw parsing_error(std::format("argument {}: invalid value: '{}'", get_name_for_error(), token)); - } - check_choices(value); - return value; - } - - auto consume_tokens(std::ranges::view auto tokens) const -> std::vector - { - auto result = std::vector(); - auto consumed = std::vector(); - for (auto & token : tokens) - { - result.push_back(process_token(token.m_token)); - consumed.push_back(&token); - } - std::ranges::for_each(consumed, [](auto token) { token->m_consumed = true; }); - return result; - } - - auto check_choices(std::any const & value) const -> void - { - if (get_choices().empty()) - { - return; - } - - if (!std::ranges::any_of( - get_choices(), - [&](auto const & rhs) { return get_type_handler().compare(value, rhs); })) - { - auto const message = std::format( - "argument {}: invalid choice: {} (choose from {})", - get_joined_names(), - get_type_handler().to_string(value), - get_joined_choices(", ")); - throw parsing_error(message); - } - } - - auto get_transformed(std::vector const & values) const -> std::any - { - return get_type_handler().transform(values); - } - - auto get_size(std::any const & value) const -> std::size_t - { - return get_type_handler().size(value); - } - - auto append_value(std::any const & value, std::any & values) const -> void - { - get_type_handler().append(value, values); - } - }; - class PositionalArgument final : public ArgumentCommonBase { private: From f206923acc0c7dcbcb9ad241136a1bc66035573d Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 17:02:17 +0200 Subject: [PATCH 7/8] Remove OptionsHolder class --- include/argparse.hpp | 73 -------------------------------------------- 1 file changed, 73 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index a8c5518..9c69dfa 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -627,79 +627,6 @@ namespace argparse std::unique_ptr type_handler = std::make_unique>(); }; - class OptionsHolder - { - protected: - explicit OptionsHolder(Options options) - : m_options(std::move(options)) - { - } - ~OptionsHolder() = default; - - auto get_names() const -> std::vector const & - { - return m_options.names; - } - - auto get_help() const -> std::string const & - { - return m_options.help; - } - - auto get_metavar() const -> std::string const & - { - return m_options.metavar; - } - - auto get_dest() const -> std::string const & - { - return m_options.dest; - } - - auto get_action() const -> Action - { - return m_options.action; - } - - auto get_const() const -> std::any const & - { - return m_options.const_; - } - - auto get_default() const -> std::any const & - { - return m_options.default_; - } - - auto get_required() const -> bool - { - return m_options.required; - } - - auto get_choices() const -> std::vector const & - { - return m_options.choices; - } - - auto get_nargs() const -> std::optional> const & - { - return m_options.nargs; - } - - auto get_mutually_exclusive_group() const -> MutuallyExclusiveGroup const * - { - return m_options.mutually_exclusive_group; - } - - auto get_type_handler() const -> TypeHandler const & - { - return *m_options.type_handler; - } - - private: - Options m_options; - }; - class ArgumentCommonImpl { public: From a96e933716b8d6fdb203297108fa8df7aadab1b6 Mon Sep 17 00:00:00 2001 From: Krzysiek Karbowiak Date: Tue, 23 Sep 2025 17:14:22 +0200 Subject: [PATCH 8/8] Move functions to protected section --- include/argparse.hpp | 49 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index 9c69dfa..efa8721 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -861,64 +861,65 @@ namespace argparse return m_impl.get_help(); } - auto get_default() const -> std::any const & + auto has_nargs() const -> bool override { - return m_impl.get_default(); + return m_impl.has_nargs(); } - auto get_const() const -> std::any const & + auto has_nargs_number() const -> bool override { - return m_impl.get_const(); + return m_impl.has_nargs_number(); } - auto get_metavar() const -> std::string const & + auto has_choices() const -> bool override { - return m_impl.get_metavar(); + return m_impl.has_choices(); } - auto get_dest() const -> std::string const & + auto get_joined_choices(std::string_view separator) const -> std::string override { - return m_impl.get_dest(); + return m_impl.get_joined_choices(separator); } - auto get_required() const -> bool + auto get_nargs_number() const -> std::size_t override { - return m_impl.get_required(); + return m_impl.get_nargs_number(); } - auto get_action() const -> Action + auto get_nargs_option() const -> Nargs override { - return m_impl.get_action(); + return m_impl.get_nargs_option(); } - auto has_nargs() const -> bool override + protected: + auto get_default() const -> std::any const & { - return m_impl.has_nargs(); + return m_impl.get_default(); } - auto has_nargs_number() const -> bool override + auto get_const() const -> std::any const & { - return m_impl.has_nargs_number(); + return m_impl.get_const(); } - auto has_choices() const -> bool override + auto get_metavar() const -> std::string const & { - return m_impl.has_choices(); + return m_impl.get_metavar(); } - auto get_joined_choices(std::string_view separator) const -> std::string override + auto get_dest() const -> std::string const & { - return m_impl.get_joined_choices(separator); + return m_impl.get_dest(); } - auto get_nargs_number() const -> std::size_t override + auto get_required() const -> bool { - return m_impl.get_nargs_number(); + return m_impl.get_required(); } - auto get_nargs_option() const -> Nargs override + auto get_action() const -> Action { - return m_impl.get_nargs_option(); + return m_impl.get_action(); } auto parse_arguments(std::ranges::view auto tokens) -> std::any