From 85e52a1dcd9f9c48496add0b5e22c2a45df30b78 Mon Sep 17 00:00:00 2001 From: "killua.z" Date: Wed, 13 May 2026 10:43:19 +0400 Subject: [PATCH] feat: accumulate to int. --- examples/test/main.cpp | 28 ++++--- src/headers/parser/argument_builder.hpp | 97 +++++++++++++++++++------ src/headers/parser/parser_v2.hpp | 38 +++++++--- src/source/parser/argument_parser.cpp | 8 +- 4 files changed, 130 insertions(+), 41 deletions(-) diff --git a/examples/test/main.cpp b/examples/test/main.cpp index 3b980a3..2552a32 100644 --- a/examples/test/main.cpp +++ b/examples/test/main.cpp @@ -9,7 +9,7 @@ #include #include -using argument = argument_parser::builder::argument<>; +using argument_parser::builder::new_argument; using argument_parser::parsing_traits::hint_type; @@ -42,9 +42,9 @@ template struct parser_trait> { using namespace argument_parser::v2::flags; auto main() -> int { - argument_parser::v2::parser parser(false); + argument_parser::v2::parser parser(true); - argument::start() + start() .positional("count") .position(1) .help_text("How many times to repeat the action.") @@ -52,13 +52,13 @@ auto main() -> int { .build(parser); int captured_value = 0; - argument::start() + new_argument() .long_argument("threshold") .help_text("Store the parsed value through a reference.") .reference(captured_value) .build(parser); - argument::start() + new_argument() .positional("captured") .position(0) .help_text("Store the parsed value through a reference.") @@ -71,7 +71,7 @@ auto main() -> int { // {Reference, &captured_value}, // }); - argument::start().short_argument("q").help_text("Store a boolean flag.").build(parser); + new_argument().short_argument("q").help_text("Store a boolean flag.").build(parser); // argument::start() // .long_argument("regex") @@ -79,14 +79,14 @@ auto main() -> int { // .store>() // .build(parser); - argument::start() + new_argument() .short_argument("e") .long_argument("echo") .help_text("Echo the parsed value.") .action(echo) .build(parser); - argument::start() + new_argument() .long_argument("vecstr") .short_argument("vs") .action>([](std::vector const &vecstr) { @@ -96,8 +96,14 @@ auto main() -> int { }) .build(parser); + auto accumulated_pos = new_argument() + .short_argument("v") + .help_text("turns on verbose execution. up to three levels of verbosity.") + .count() + .build_and_get(parser); + auto accumulate_vec = - argument::start().long_argument("vecstr1").short_argument("vs1").accumulate().build_and_get(parser); + new_argument().long_argument("vecstr1").short_argument("vs1").accumulate().build_and_get(parser); parser.add_argument>({ {LongArgument, "accumulate"}, @@ -152,5 +158,9 @@ auto main() -> int { } } + if (accumulated_pos) { + std::cout << "accumulated_pos: " << *accumulated_pos << '\n'; + } + return 0; } diff --git a/src/headers/parser/argument_builder.hpp b/src/headers/parser/argument_builder.hpp index 80fb193..afe3b55 100644 --- a/src/headers/parser/argument_builder.hpp +++ b/src/headers/parser/argument_builder.hpp @@ -6,8 +6,8 @@ #include #include -#ifndef ARGUMENT_PARSER_PARSER_V3_HPP -#define ARGUMENT_PARSER_PARSER_V3_HPP +#ifndef ARGUMENT_PARSER_PARSER_BUILDER_HPP +#define ARGUMENT_PARSER_PARSER_BUILDER_HPP namespace argument_parser::builder { @@ -21,6 +21,7 @@ namespace argument_parser::builder { flag, reference, accumulate, + count, nonparametered_action, parametered_action }; @@ -44,12 +45,17 @@ namespace argument_parser::builder { constexpr mask_type required = bit(v2_flag::Required); constexpr mask_type reference = bit(v2_flag::Reference); constexpr mask_type accumulate = bit(v2_flag::Accumulate); + constexpr mask_type count = bit( + static_cast(static_cast(v2_flag::Accumulate) + (static_cast(extra_capability::Store) | + static_cast(extra_capability::Flag)))); constexpr mask_type store = bit(extra_capability::Store); constexpr mask_type flag = bit(extra_capability::Flag); - constexpr mask_type value_mode_group = action | reference | accumulate | store | flag; + constexpr mask_type value_mode_group = action | reference | accumulate | count | store | flag; constexpr mask_type initial = short_argument | long_argument | positional | help_text | action | required | - reference | accumulate | store | flag; + reference | accumulate | count | store | flag; + + constexpr mask_type storable_mode_group = (count | store | flag | accumulate | reference) >> 1; constexpr auto has(mask_type mask, mask_type capability) -> bool { return (mask & capability) == capability; @@ -70,6 +76,10 @@ namespace argument_parser::builder { constexpr auto is_buildable(mask_type mask) -> bool { return has_selected_identifier(mask); } + + constexpr auto is_build_and_gettable(mask_type mask) -> bool { + return has_selected_identifier(mask) && has(mask, storable_mode_group); + } } // namespace builder_mask template class container { @@ -158,7 +168,7 @@ namespace argument_parser::builder { using next_argument = argument; @@ -214,11 +224,13 @@ namespace argument_parser::builder { template = 0> - auto accumulate() const - -> argument> { + auto accumulate() const -> argument> { using vector_type = std::vector; - using next_argument = - argument; + using next_argument = argument; next_argument next{*this}; next.m_value_mode = value_mode::accumulate; @@ -227,12 +239,14 @@ namespace argument_parser::builder { template = 0, typename T> - auto accumulate(T &value) const - -> argument { + auto accumulate(T &value) const -> argument< + builder_mask::replace(current_mask, builder_mask::value_mode_group, builder_mask::storable_mode_group), T> { static_assert(argument_parser::v2::deducers::is_vector_v, "accumulate(target) requires a std::vector target."); - using next_argument = argument; + using next_argument = argument; next_argument next{*this}; next.m_reference = std::addressof(value); @@ -240,10 +254,44 @@ namespace argument_parser::builder { return next; } + template = 0> + auto count() const -> argument { + using next_argument = argument; + + next_argument next{*this}; + next.m_value_mode = value_mode::count; + return next; + } + + template = 0> + auto count(int &value) const -> argument { + + using next_argument = argument; + + next_argument next{*this}; + next.m_reference = std::addressof(value); + next.m_value_mode = value_mode::count; + return next; + } + template = 0> - auto flag() const -> argument { - using next_argument = argument; + auto flag() const -> argument { + using next_argument = argument; next_argument next{*this}; next.m_value_mode = value_mode::flag; @@ -252,9 +300,11 @@ namespace argument_parser::builder { template = 0, typename T> - auto reference(T &value) const - -> argument { - using next_argument = argument; + auto reference(T &value) const -> argument< + builder_mask::replace(current_mask, builder_mask::value_mode_group, builder_mask::storable_mode_group), T> { + using next_argument = argument; next_argument next{*this}; next.m_reference = std::addressof(value); @@ -318,6 +368,7 @@ namespace argument_parser::builder { } break; case value_mode::accumulate: + case value_mode::count: if constexpr (!std::is_same_v) { build_accumulate(parser); return; @@ -341,7 +392,8 @@ namespace argument_parser::builder { throw std::logic_error("The builder reached build() without a supported terminal value mode."); } - template = 0> + template = 0> auto build_and_get(argument_parser::v2::base_parser &parser) const -> container { assert_has_identifier(); switch (m_value_mode) { @@ -349,6 +401,7 @@ namespace argument_parser::builder { case value_mode::flag: case value_mode::unresolved: case value_mode::accumulate: + case value_mode::count: build(parser); break; default: @@ -380,9 +433,7 @@ namespace argument_parser::builder { m_required(other.m_required), m_action(other.m_action), m_reference(copy_reference(other.m_reference)), m_value_mode(other.m_value_mode) {} - template - using typed_map = - std::unordered_map>; + template using typed_map = std::unordered_map>; using non_typed_map = std::unordered_map; @@ -538,6 +589,10 @@ namespace argument_parser::builder { template friend class argument; }; + static inline auto new_argument() { + return argument<>::start(); + } + namespace assertions { struct noop_handler { void operator()() const {} diff --git a/src/headers/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp index 24a10fe..46284d4 100644 --- a/src/headers/parser/parser_v2.hpp +++ b/src/headers/parser/parser_v2.hpp @@ -195,8 +195,10 @@ namespace argument_parser::v2 { found_params[extended_add_argument_flags::Action] = true; accumulates = true; if constexpr (!std::is_same_v) { - if constexpr (!deducers::is_vector_v) { - throw std::logic_error("Expected vector (type does not have value_type member)"); + if constexpr (std::is_same_v) { + action = make_accumulate_action(argument_pairs, ref_mode, short_arg, long_arg); + } else if constexpr (!deducers::is_vector_v) { + throw std::logic_error("Expected vector or integer type"); } else { if (action && !ref_mode) { throw std::logic_error("Cannot use both action and accumulate for the same argument"); @@ -454,10 +456,14 @@ namespace argument_parser::v2 { } template std::unique_ptr make_accumulate_ref_action(Vector *target) { - using Value = typename Vector::value_type; - return helpers::make_parametered_action( - [target](Value const &value) { target->emplace_back(value); }) - .clone(); + if constexpr (std::is_same_v) { + return helpers::make_non_parametered_action([target]() { *target += 1; }).clone(); + } else { + using Value = typename Vector::value_type; + return helpers::make_parametered_action( + [target](Value const &value) { target->emplace_back(value); }) + .clone(); + } } template @@ -466,8 +472,14 @@ namespace argument_parser::v2 { on_complete( [this, short_arg = std::move(short_arg), long_arg = std::move(long_arg), accumulation_target](auto const &) { - if (accumulation_target->empty()) { - return; + if constexpr (std::is_same_v) { + if (*accumulation_target == 0) { + return; + } + } else { + if (accumulation_target->empty()) { + return; + } } const auto sid = this->find_argument_id(short_arg); @@ -484,8 +496,14 @@ namespace argument_parser::v2 { void store_accumulated_on_complete(std::string positional_name, std::shared_ptr accumulation_target) { on_complete( [this, positional_name = std::move(positional_name), accumulation_target](auto const &) { - if (accumulation_target->empty()) { - return; + if constexpr (std::is_same_v) { + if (*accumulation_target == 0) { + return; + } + } else { + if (accumulation_target->empty()) { + return; + } } auto id = this->find_argument_id(positional_name); diff --git a/src/source/parser/argument_parser.cpp b/src/source/parser/argument_parser.cpp index 10a274a..9c7ecb0 100644 --- a/src/source/parser/argument_parser.cpp +++ b/src/source/parser/argument_parser.cpp @@ -130,11 +130,17 @@ namespace argument_parser { auto name_it = reverse_positional_names.find(pos_id); if (name_it == reverse_positional_names.end()) continue; - if (auto const &arg = argument_map.at(pos_id); arg.is_required()) { + + auto const &arg = argument_map.at(pos_id); + if (arg.is_required()) { ss << " <" << name_it->second << ">"; } else { ss << " [" << name_it->second << "]"; } + + if (arg.is_positional_accumulator()) { + ss << "..."; + } } ss << "\n";