From 34bd80971bbb35770909ecfa26acc7f146646c09 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Thu, 23 Jul 2020 21:02:33 -0700 Subject: [PATCH] allow the use of `allow_extra_args(false)` to specify only a single argument at a time with a container output. (#484) --- README.md | 1 + include/CLI/App.hpp | 7 ++++++- tests/OptionTypeTest.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d3552752..2d64d3cb 100644 --- a/README.md +++ b/README.md @@ -322,6 +322,7 @@ Before parsing, you can set the following options: - `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments). - `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone". This does not apply to short form options since they only have one character - `->disable_flag_override()`: From the command line long form flag options can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line. The `=` does not work with short form flag options. +- `->allow_extra_args(true/false)`: 🚧 If set to true the option will take an unlimited number of arguments like a vector, if false it will limit the number of arguments to the size of the type used in the option. Default value depends on the nature of the type use, containers default to true, others default to false. - `->delimiter(char)`: Allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value. - `->description(str)`: Set/change the description. - `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`,`->take_all()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy). diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 8d47976e..6534a2b2 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -2761,7 +2761,12 @@ class App { } int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min()); int max_num = op->get_items_expected_max(); - + // check container like options to limit the argument size to a single type if the allow_extra_flags argument is + // set. 16 is somewhat arbitrary (needs to be at least 4) + if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) { + auto tmax = op->get_type_size_max(); + max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size; + } // Make sure we always eat the minimum for unlimited vectors int collected = 0; // total number of arguments collected int result_count = 0; // local variable for number of results in a single arg string diff --git a/tests/OptionTypeTest.cpp b/tests/OptionTypeTest.cpp index 4ee7deb8..e78d10a7 100644 --- a/tests/OptionTypeTest.cpp +++ b/tests/OptionTypeTest.cpp @@ -838,3 +838,29 @@ TEST_F(TApp, tupleTwoVectors) { EXPECT_EQ(std::get<0>(cv).size(), 2U); EXPECT_EQ(std::get<1>(cv).size(), 3U); } + +TEST_F(TApp, vectorSingleArg) { + + std::vector cv; + app.add_option("-c", cv)->allow_extra_args(false); + std::string extra; + app.add_option("args", extra); + args = {"-c", "1", "-c", "2", "4"}; + + run(); + EXPECT_EQ(cv.size(), 2U); + EXPECT_EQ(extra, "4"); +} + +TEST_F(TApp, vectorDoubleArg) { + + std::vector> cv; + app.add_option("-c", cv)->allow_extra_args(false); + std::vector extras; + app.add_option("args", extras); + args = {"-c", "1", "bob", "-c", "2", "apple", "4", "key"}; + + run(); + EXPECT_EQ(cv.size(), 2U); + EXPECT_EQ(extras.size(), 2U); +}