diff --git a/README.md b/README.md index 6b45a550..65d97a29 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ While all options internally are the same type, there are several ways to add an app.add_option(option_name, help_str="") app.add_option(option_name, - variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectorof any other supported type. + variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectors of any other supported type. help_string="") app.add_option_function(option_name, @@ -363,7 +363,7 @@ Before parsing, you can set the following 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). `->join(delim)` can also be used to join with a specific delimiter. This equivalent to calling `->delimiter(delim)` and `->join()` +* `->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). `->join(delim)` can also be used to join with a specific delimiter. This equivalent to calling `->delimiter(delim)` and `->join()`. Valid values are `CLI::MultiOptionPolicy::Throw`, `CLI::MultiOptionPolicy::Throw`, `CLI::MultiOptionPolicy::TakeLast`, `CLI::MultiOptionPolicy::TakeFirst`, `CLI::MultiOptionPolicy::Join`, `CLI::MultiOptionPolicy::TakeAll`, and `CLI::MultiOptionPolicy::Sum` 🚧. * `->check(std::string(const std::string &), validator_name="",validator_description="")`: Define a check function. The function should return a non empty string with the error message if the check fails * `->check(Validator)`: Use a Validator object to do the check see [Validators](#validators) for a description of available Validators and how to create new ones. * `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options. diff --git a/book/chapters/flags.md b/book/chapters/flags.md index 4e269acd..fe837b17 100644 --- a/book/chapters/flags.md +++ b/book/chapters/flags.md @@ -15,7 +15,7 @@ This will bind the flag `-f` to the boolean `my_flag`. After the parsing step, ` ## Integer flags -If you want to allow multiple flags, simply use any integer-like instead of a bool: +If you want to allow multiple flags and count their value, simply use any integral variables instead of a bool: ```cpp int my_flag{0}; @@ -24,6 +24,8 @@ app.add_flag("-f", my_flag, "Optional description"); After the parsing step, `my_flag` will contain the number of times this flag was found on the command line, including 0 if not found. +This behavior can also be controlled manually via `->multi_option_policy(CLI::MultiOptionPolicy::Sum)` as of version 2.2. + ## Arbitrary type flags CLI11 allows the type of the variable to assign to in the `add_flag` function to be any supported type. This is particularly useful in combination with specifying default values for flags. The allowed types include bool, int, float, vector, enum, or string-like. diff --git a/book/chapters/options.md b/book/chapters/options.md index a71ad428..85eef6f1 100644 --- a/book/chapters/options.md +++ b/book/chapters/options.md @@ -171,7 +171,7 @@ When you call `add_option`, you get a pointer to the added option. You can use t | `->allow_extra_args()` | Allow extra argument values to be included when an option is passed. Enabled by default for vector options. | | `->disable_flag_override()` | specify that flag options cannot be overridden on the command line use `=` | | `->delimiter('')` | specify a character that can be used to separate elements in a command line argument, default is , common values are ',', and ';' | -| `->multi_option_policy( CLI::MultiOptionPolicy::Throw)` | Sets the policy for handling multiple arguments if the option was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`, and `Join` are also available. See the next four lines for shortcuts to set this more easily. | +| `->multi_option_policy( CLI::MultiOptionPolicy::Throw)` | Sets the policy for handling multiple arguments if the option was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`, `Join`, and `Sum` are also available. See the next four lines for shortcuts to set this more easily. | | `->take_last()` | Only use the last option if passed several times. This is always true by default for bool options, regardless of the app default, but can be set to false explicitly with `->multi_option_policy()`. | | `->take_first()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst)` | | `->take_all()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeAll)` | @@ -211,7 +211,7 @@ One of CLI11's systems to allow customizability without high levels of verbosity * `group`: The group name starts as "Options" * `required`: If the option must be given. Defaults to `false`. Is ignored for flags. -* `multi_option_policy`: What to do if several copies of an option are passed and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This is also used for bool flags, but they always are created with the value `CLI::MultiOptionPolicy::TakeLast` regardless of the default, so that multiple bool flags does not cause an error. But you can override that flag by flag. +* `multi_option_policy`: What to do if several copies of an option are passed and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This is also used for bool flags, but they always are created with the value `CLI::MultiOptionPolicy::TakeLast` or `CLI::MultiOptionPolicy::Sum` regardless of the default, so that multiple bool flags does not cause an error. But you can override that setting by calling the `multi_option_policy` directly. * `ignore_case`: Allow any mixture of cases for the option or flag name * `ignore_underscore`: Allow any number of underscores in the option or flag name * `configurable`: Specify whether an option can be configured through a config file diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index fb6d6d12..5b438b19 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -61,6 +61,22 @@ class App; using App_p = std::shared_ptr; +namespace detail { +/// helper functions for adding in appropriate flag modifiers for add_flag + +template ::value || (sizeof(T) <= 1U), detail::enabler> = detail::dummy> +Option *default_flag_modifiers(Option *opt) { + return opt->always_capture_default(); +} + +/// summing modifiers +template ::value && (sizeof(T) > 1U), detail::enabler> = detail::dummy> +Option *default_flag_modifiers(Option *opt) { + return opt->multi_option_policy(MultiOptionPolicy::Sum)->default_str("0")->force_callback(); +} + +} // namespace detail + class Option_group; /// Creates a command line program, with very few defaults. /** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated @@ -807,43 +823,21 @@ class App { return _add_flag_internal(flag_name, CLI::callback_t(), flag_description); } - /// Add option for flag with integer result - defaults to allowing multiple passings, but can be forced to one - /// if `multi_option_policy(CLI::MultiOptionPolicy::Throw)` is used. - template < - typename T, - enable_if_t::value && !std::is_const::value && !is_bool::value, - detail::enabler> = detail::dummy> - Option *add_flag(std::string flag_name, - T &flag_count, ///< A variable holding the count - std::string flag_description = "") { - flag_count = 0; - CLI::callback_t fun = [&flag_count](const CLI::results_t &res) { - try { - detail::sum_flag_vector(res, flag_count); - } catch(const std::invalid_argument &) { - return false; - } - return true; - }; - return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description)) - ->multi_option_policy(MultiOptionPolicy::TakeAll); - } - /// Other type version accepts all other types that are not vectors such as bool, enum, string or other classes /// that can be converted from a string template ::value && !std::is_const::value && - (!std::is_constructible::value || is_bool::value) && !std::is_constructible, T>::value, detail::enabler> = detail::dummy> Option *add_flag(std::string flag_name, - T &flag_result, ///< A variable holding true if passed + T &flag_result, ///< A variable holding the flag result std::string flag_description = "") { CLI::callback_t fun = [&flag_result](const CLI::results_t &res) { return CLI::detail::lexical_cast(res[0], flag_result); }; - return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))->run_callback_for_default(); + auto *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description)); + return detail::default_flag_modifiers(opt); } /// Vector version to capture multiple flags. @@ -888,13 +882,13 @@ class App { std::string flag_description = "") { CLI::callback_t fun = [function](const CLI::results_t &res) { - std::int64_t flag_count = 0; - detail::sum_flag_vector(res, flag_count); + std::int64_t flag_count{0}; + CLI::detail::lexical_cast(res[0], flag_count); function(flag_count); return true; }; return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description)) - ->multi_option_policy(MultiOptionPolicy::TakeAll); + ->multi_option_policy(MultiOptionPolicy::Sum); } #ifdef CLI11_CPP14 diff --git a/include/CLI/Option.hpp b/include/CLI/Option.hpp index c78fc057..5ca1e0f5 100644 --- a/include/CLI/Option.hpp +++ b/include/CLI/Option.hpp @@ -40,7 +40,8 @@ enum class MultiOptionPolicy : char { TakeLast, //!< take only the last Expected number of arguments TakeFirst, //!< take only the first Expected number of arguments Join, //!< merge all the arguments together into a single string via the delimiter character default('\n') - TakeAll //!< just get all the passed argument regardless + TakeAll, //!< just get all the passed argument regardless + Sum //!< sum all the arguments together if numerical or concatenate directly without delimiter }; /// This is the CRTP base class for Option and OptionDefaults. It was designed this way @@ -1266,6 +1267,9 @@ class Option : public OptionBase