diff --git a/README.md b/README.md index fb0ceb72c..0b9991bd6 100644 --- a/README.md +++ b/README.md @@ -195,14 +195,15 @@ 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, int, float, vector, enum, 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 πŸ†•. + variable_to_bind_to, // bool, int, float, vector, enum, 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 vector🚧 of any other supported type help_string="") app.add_option_function(option_name, function , // type can be any type supported by add_option help_string="") -app.add_complex(... // Special case: support for complex numbers +app.add_complex(... // Special case: support for complex numbers ⚠️. complex numbers are now fully supported in the add_option so this function is redundant. + // πŸ†• There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type app.add_option(option_name, T &output, // output must be assignable or constructible from a value of type XC @@ -213,7 +214,7 @@ app.add_flag(option_name, help_string="") app.add_flag(option_name, - variable_to_bind_to, // bool, int, float, vector, enum, or string-like, or any singular object with a defined conversion from a string like add_option + variable_to_bind_to, // bool, int, float,complex, containers, enum, or string-like, or any singular object with a defined conversion from a string like add_option help_string="") app.add_flag_function(option_name, @@ -245,9 +246,9 @@ app.add_option("--vs",v1); app.add_option("--vi",v1); app.add_option("--vf",v1); ``` -otherwise the output would default to a string. The `add_option` can be used with any integral or floating point types, enumerations, or strings. Or any type that takes an int, double, or std::string in an assignment operator or constructor. If an object can take multiple varieties of those, std::string takes precedence, then double then int. To better control which one is used or to use another type for the underlying conversions use the two parameter template to directly specify the conversion type. +otherwise the output would default to a string. The `add_option` can be used with any integral or floating point types, enumerations, or strings. Or any type that takes an int, double, or std\::string in an assignment operator or constructor. If an object can take multiple varieties of those, std::string takes precedence, then double then int. To better control which one is used or to use another type for the underlying conversions use the two parameter template to directly specify the conversion type. -Types such as (std or boost) `optional`, `optional`, and `optional` are supported directly, other optional types can be added using the two parameter template. See [CLI11 Advanced Topics/Custom Converters][] for information on how this could be done and how you can add your own converters for additional types. +Types such as (std or boost) `optional`, `optional`, and `optional` and any other wrapper types are supported directly. For purposes of CLI11 wrapper types are those which `value_type` definition. See [CLI11 Advanced Topics/Custom Converters][] for information on how you can add your own converters for additional types. Vector types can also be used in the two parameter template overload ``` @@ -308,7 +309,7 @@ Before parsing, you can set the following options: - `->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. - `->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()`, 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). +- `->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). - `->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. @@ -319,7 +320,7 @@ Before parsing, you can set the following options: - `->default_function(std::string())`: Advanced: Change the function that `capture_default_str()` uses. - `->always_capture_default()`: Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`. - `default_str(string)`: Set the default string directly. This string will also be used as a default value if no arguments are passed and the value is requested. -- `default_val(value)`: πŸ†• Generate the default string from a value and validate that the value is also valid. For options that assign directly to a value type the value in that type is also updated. Value must be convertible to a string(one of known types or a stream operator). +- `default_val(value)`: πŸ†• Generate the default string from a value and validate that the value is also valid. For options that assign directly to a value type the value in that type is also updated. Value must be convertible to a string(one of known types or have a stream operator). These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. The `each` function takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `each`, `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. Operations added through `transform` are executed first in reverse order of addition, and `check` and `each` are run following the transform functions in order of addition. If you just want to see the unconverted values, use `.results()` to get the `std::vector` of results. diff --git a/book/chapters/options.md b/book/chapters/options.md index 571240de2..149cd654b 100644 --- a/book/chapters/options.md +++ b/book/chapters/options.md @@ -9,7 +9,7 @@ int int_option{0}; app.add_option("-i", int_option, "Optional description"); ``` -This will bind the option `-i` to the integer `int_option`. On the command line, a single value that can be converted to an integer will be expected. Non-integer results will fail. If that option is not given, CLI11 will not touch the initial value. This allows you to set up defaults by simply setting your value beforehand. If you want CLI11 to display your default value, you can add the optional final argument `true` when you add the option. If you do not add this, you do not even need your option value to be printable[^1]. +This will bind the option `-i` to the integer `int_option`. On the command line, a single value that can be converted to an integer will be expected. Non-integer results will fail. If that option is not given, CLI11 will not touch the initial value. This allows you to set up defaults by simply setting your value beforehand. If you want CLI11 to display your default value, you can add the optional final argument `true` when you add the option. ```cpp int int_option{0}; @@ -20,11 +20,15 @@ You can use any C++ int-like type, not just `int`. CLI11 understands the followi | Type | CLI11 | |-------------|-------| -| int-like | Integer conversion up to 64-bit, can be unsigned | -| float-like | Floating point conversions | -| string-like | Anything else that can be shifted into a StringStream | -| vector-like | A vector of the above three types (see below) | +| number like | Integers, floats, bools, or any type that can be constructed from an integer or floating point number | +| string-like | std\::string, or anything that can be constructed from or assigned a std\::string | +| complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" | +| enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) | +| container-like | a container(like vector) of any available types including other containers | +| wrapper | any other object with a `value_type` static definition where the type specified by `value_type` is one of type in this list | +| tuple | a tuple, pair, or array, or other type with a tuple size and tuple_type operations defined and the members being a type contained in this list | | function | A function that takes an array of strings and returns a string that describes the conversion failure or empty for success. May be the empty function. (`{}`) | +| streamable | any other type with a `<<` operator will also work | By default, CLI11 will assume that an option is optional, and one value is expected if you do not use a vector. You can change this on a specific option using option modifiers. @@ -46,15 +50,15 @@ To make a positional option, you simply give CLI11 one name that does not start This would make two short option aliases, two long option alias, and the option would be also be accepted as a positional. -## Vectors of options +## Containers of options -If you use a vector instead of a plain option, you can accept more than one value on the command line. By default, a vector accepts as many options as possible, until the next value that could be a valid option name. You can specify a set number using an option modifier `->expected(N)`. (The default unlimited behavior on vectors is restore with `N=-1`) CLI11 does not differentiate between these two methods for unlimited acceptance options:[^2] +If you use a vector or other container instead of a plain option, you can accept more than one value on the command line. By default, a container accepts as many options as possible, until the next value that could be a valid option name. You can specify a set number using an option modifier `->expected(N)`. (The default unlimited behavior on vectors is restored with `N=-1`) CLI11 does not differentiate between these two methods for unlimited acceptance options. | Separate names | Combined names | |-------------------|-----------------| | `--vec 1 --vec 2` | `--vec 1 2` | -The original version did allow the option system to access information on the grouping of options received, but was removed for simplicity. +It is also possible to specify a minimum and maximum number through `->expected(Min,Max)`. It is also possible to specify a min and max type size for the elements of the container. It most cases these values will be automatically determined but a user can manually restrict them. An example of setting up a vector option: @@ -65,6 +69,43 @@ app.add_option("--vec", int_vec, "My vector option"); Vectors will be replaced by the parsed content if the option is given on the command line. +A definition of a container for purposes of CLI11 is a type with a `end()`, `insert(...)`, `clear()` and `value_type` definitions. This includes `vector`, `set`, `deque`, `list`, `forward_iist`, `map`, `unordered_map` and a few others from the standard library, and many other containers from the boost library. + +### containers of containers +Containers of containers are also supported. +```cpp +std::vector> int_vec; +app.add_option("--vec", int_vec, "My vector of vectors option"); +``` +CLI11 inserts a separator sequence at the start of each argument call to separate the vectors. So unless the separators are injected as part of the command line each call of the option on the command line will result in a separate element of the outer vector. This can be manually controlled via `inject_separator(true|false)` but in nearly all cases this should be left to the defaults. To insert of a separator from the command line add a `%%` where the separation should occur. +``` +cmd --vec_of_vec 1 2 3 4 %% 1 2 +``` +would then result in a container of size 2 with the first element containing 4 values and the second 2. + +This separator is also the only way to get values into something like +```cpp +std::pair,std::vector> two_vecs; +app.add_option("--vec", two_vecs, "pair of vectors"); +``` +without calling the argument twice. + +Further levels of nesting containers should compile but intermediate layers will only have a single element in the container, so is probably not that useful. + +### Nested types +Types can be nested For example +```cpp +std::map> map; +app.add_option("--dict", map, "map of pairs"); +``` + +will require 3 arguments for each invocation, and multiple sets of 3 arguments can be entered for a single invocation on the command line. + +```cpp +std::map>> map; +app.add_option("--dict", map, "map of pairs"); +``` +will result in a requirement for 2 integers on each invocation and absorb an unlimited number of strings including 0. ## Option modifiers @@ -75,25 +116,32 @@ When you call `add_option`, you get a pointer to the added option. You can use t | `->required()` | The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works. | | `->expected(N)` | Take `N` values instead of as many as possible, mainly for vector args. | | `->expected(Nmin,Nmax)` | Take between `Nmin` and `Nmax` values. | +| `->type_size(N)` | specify that each block of values would consist of N elements | +| `->type_size(Nmin,Nmax)` | specify that each block of values would consist of between Nmin and Nmax elements | | `->needs(opt)` | This option requires another option to also be present, opt is an `Option` pointer. | | `->excludes(opt)` | This option cannot be given with `opt` present, opt is an `Option` pointer. | | `->envname(name)` | Gets the value from the environment if present and not passed on the command line. | | `->group(name)` | The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `"Hidden"` will not show up in the help print. | +| `->description(string)` | Set/change the description | | `->ignore_case()` | Ignore the case on the command line (also works on subcommands, does not affect arguments). | -| `->ignore_underscore()` | Ignore any underscores on the command line (also works on subcommands, does not affect arguments, new in CLI11 1.7). | +| `->ignore_underscore()` | Ignore any underscores on the command line (also works on subcommands, does not affect arguments). | | `->allow_extra_args()` | Allow extra argument values to be included when an option is passed. Enabled by default for vector options. | -| `->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 three lines for shortcuts to set this more easily. | +| `->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. | | `->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)` | | `->join()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses newlines or the specified delimiter to join all arguments into a single string output. | -| `->join(delim)` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses `delim` to join all arguments into a single string output. | -| `->check(CLI::ExistingFile)` | Requires that the file exists if given. | -| `->check(CLI::ExistingDirectory)` | Requires that the directory exists. | -| `->check(CLI::NonexistentPath)` | Requires that the path does not exist. | -| `->check(CLI::Range(min,max))` | Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0. | +| `->join(delim)` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses `delim` to join all arguments into a single string output. this also sets the delimiter | +| `->check(Validator)` | perform a check on the returned results to verify they meet some criteria. See [Validators](./validators.md) for more info | +| `->transform(Validator)` | Run a transforming validator on each value passed. See [Validators](./validators.md) for more info | | `->each(void(std::string))` | Run a function on each parsed value, *in order*. | +| `->default_str(string)` | set a default string for use in the help and as a default value if no arguments are passed and a value is requested | +| `->default_function(string())` | Advanced: Change the function that `capture_default_str()` uses. | +| `->default_val(value)` | Generate the default string from a value and validate that the value is also valid. For options that assign directly to a value type the value in that type is also updated. Value must be convertible to a string(one of known types or have a stream operator). | -The `->check(...)` modifiers adds a callback function of the form `bool function(std::string)` that runs on every value that the option receives, and returns a value that tells CLI11 whether the check passed or failed. +The `->check(...)` and `->transform(...)` modifiers can also take a callback function of the form `bool function(std::string)` that runs on every value that the option receives, and returns a value that tells CLI11 whether the check passed or failed. ## Using the `CLI::Option` pointer @@ -110,12 +158,17 @@ if(* opt) ## Inheritance of defaults -One of CLI11's systems to allow customizability without high levels of verbosity is the inheritance system. You can set default values on the parent `App`, and all options and subcommands created from it remember the default values at the point of creation. The default value for Options, specifically, are accessible through the `option_defaults()` method. There are four settings that can be set and inherited: +One of CLI11's systems to allow customizability without high levels of verbosity is the inheritance system. You can set default values on the parent `App`, and all options and subcommands created from it remember the default values at the point of creation. The default value for Options, specifically, are accessible through the `option_defaults()` method. There are a number of settings that can be set and inherited: * `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. * `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 +* `disable_flag_override`: do not allow flag values to be overridden on the command line +* `always_capture_default`: specify that the default values should be automatically captured. +* `delimiter`: A delimiter to use for capturing multiple values in a single command line string (e.g. --flag="flag,-flag2,flag3") An example of usage: @@ -129,29 +182,7 @@ app.get_group() // is "Required" Groups are mostly for visual organization, but an empty string for a group name will hide the option. -## Listing of specialty options: - -Besides `add_option` and `add_flag`, there are several special ways to create options for sets and complex numbers. - -### Sets - -You can add a set with `add_set`, where you give a variable to set and a `std::set` of choices to pick from. There also is a `add_set_ignore_case` version which ignores case when set matching. If you use an existing set instead of an inline one, you can edit the set after adding it and changes will be reflected in the set checking and help message. - -```cpp -int val{0}; -app.add_set("--even", val, {0,2,4,6,8}); -``` - -### Complex numbers - -You can also add a complex number. This type just needs to support a `(T x, T y)` constructor and be printable. You can also pass one extra argument that will set the label of the type; by default it is "COMPLEX". - -```cpp -std::complex val{0.0F,0.0F}; -app.add_complex("--cplx", val); -``` - -### Windows style options (New in CLI11 1.7) +### Windows style options You can also set the app setting `app->allow_windows_style_options()` to allow windows style options to also be recognized on the command line: @@ -160,12 +191,13 @@ You can also set the app setting `app->allow_windows_style_options()` to allow w * `/long` (long flag) * `/file filename` (space) * `/file:filename` (colon) +* `/long_flag:false` (long flag with : to override the default value) -Windows style options do not allow combining short options or values not separated from the short option like with `-` options. You still specify option names in the same manor as on Linux with single and double dashes when you use the `add_*` functions, and the Linux style on the command line will still work. If a long and a short option share the same name, the option will match on the first one defined. +Windows style options do not allow combining short options or values not separated from the short option like with `-` options. You still specify option names in the same manner as on Linux with single and double dashes when you use the `add_*` functions, and the Linux style on the command line will still work. If a long and a short option share the same name, the option will match on the first one defined. ## Parse configuration -How an option and its arguments are parsed depends on a set of controls that are part of the option structure. In most circumstances these controls are set automatically based on the function used to create the option and the type the arguments are parsed into. The variables define the size of the underlying type (essentially how many strings make up the type), the expected size (how many groups are expected) and a flag indicating if multiple groups are allowed with a single option. And these interact with the `multi_option_policy` when it comes time to parse. +How an option and its arguments are parsed depends on a set of controls that are part of the option structure. In most circumstances these controls are set automatically based on the type or function used to create the option and the type the arguments are parsed into. The variables define the size of the underlying type (essentially how many strings make up the type), the expected size (how many groups are expected) and a flag indicating if multiple groups are allowed with a single option. And these interact with the `multi_option_policy` when it comes time to parse. ### examples How options manage this is best illustrated through some examples @@ -173,7 +205,7 @@ How options manage this is best illustrated through some examples std::string val; app.add_option("--opt",val,"description"); ``` -creates an option that assigns a value to a `std::string` When this option is constructed it sets a type_size of 1. meaning that the assignment uses a single string. The Expected size is also set to 1 by default, and `allow_extra_args` is set to false. meaning that each time this option is called 1 argument is expected. This would also be the case if val were a `double`, `int` or any other single argument types. +creates an option that assigns a value to a `std::string` When this option is constructed it sets a type_size min and max of 1. Meaning that the assignment uses a single string. The Expected size is also set to 1 by default, and `allow_extra_args` is set to false. meaning that each time this option is called 1 argument is expected. This would also be the case if val were a `double`, `int` or any other single argument types. now for example ```cpp @@ -203,6 +235,20 @@ app.add_flag("--opt",val,"description"); Using the add_flag methods for creating options creates an option with an expected size of 0, implying no arguments can be passed. +```cpp +std::complex val; +app.add_option("--opt",val,"description"); +``` + +triggers the complex number type which has a min of 1 and max of 2, so 1 or 2 strings can be passed. Complex number conversion supports arguments of the form "1+2j" or "1","2", or "1" "2i". The imaginary number symbols `i` and `j` are interchangeable in this context. + + +```cpp +std::vector> val; +app.add_option("--opt",val,"description"); +``` +has a type size of 1 to (1<<30). + ### Customization The `type_size(N)`, `type_size(Nmin, Nmax)`, `expected(N)`, `expected(Nmin,Nmax)`, and `allow_extra_args()` can be used to customize an option. For example @@ -214,5 +260,9 @@ opt->expected(0,1); ``` will create a hybrid option, that can exist on its own in which case the value "vvv" is used or if a value is given that value will be used. -[^1]: For example, enums are not printable to `std::cout`. -[^2]: There is a small difference. An combined unlimited option will not prioritize over a positional that could still accept values. +There are some additional options that can be specified to modify an option for specific cases +- `->run_callback_for_default()` will specify that the callback should be executed when a default_val is set. This is set automatically when appropriate though it can be turned on or off and any user specified callback for an option will be executed when the default value for an option is set. + +## Unusual circumstances +There are a few cases where some things break down in the type system managing options and definitions. Using the `add_option` method defines a lambda function to extract a default value if required. In most cases this either straightforward or a failure is detected automatically and handled. But in a few cases a streaming template is available that several layers down may not actually be defined. The conditions in CLI11 cannot detect this circumstance automatically and will result in compile error. One specific known case is `boost::optional` if the boost optional_io header is included. This header defines a template for all boost optional values even if they do no actually have a streaming operator. For example `boost::optional` does not have a streaming operator but one is detected since it is part of a template. For these cases a secondary method `app->add_option_no_stream(...)` is provided that bypasses this operation completely and should compile in these cases. + diff --git a/extern/googletest b/extern/googletest index 2fe3bd994..703bd9caa 160000 --- a/extern/googletest +++ b/extern/googletest @@ -1 +1 @@ -Subproject commit 2fe3bd994b3189899d93f1d5a881e725e046fdc2 +Subproject commit 703bd9caab50b139428cea1aaff9974ebee5742e diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 882046afc..2caabff45 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -610,21 +610,39 @@ class App { // to structs used in the evaluation can be temporary so that would cause issues. auto Tcount = detail::type_count::value; auto XCcount = detail::type_count::value; - opt->type_size((std::max)(Tcount, XCcount)); + opt->type_size(detail::type_count_min::value, (std::max)(Tcount, XCcount)); opt->expected(detail::expected_count::value); opt->run_callback_for_default(); return opt; } + /// Add option for assigning to a variable + template ::value, detail::enabler> = detail::dummy> + Option *add_option_no_stream(std::string option_name, + AssignTo &variable, ///< The variable to set + std::string option_description = "") { + + auto fun = [&variable](const CLI::results_t &res) { // comment for spacing + return detail::lexical_conversion(res, variable); + }; + + Option *opt = add_option(option_name, fun, option_description, false, []() { return std::string{}; }); + opt->type_name(detail::type_name()); + opt->type_size(detail::type_count_min::value, detail::type_count::value); + opt->expected(detail::expected_count::value); + opt->run_callback_for_default(); + return opt; + } + /// Add option for a callback of a specific type - template + template Option *add_option_function(std::string option_name, - const std::function &func, ///< the callback to execute + const std::function &func, ///< the callback to execute std::string option_description = "") { auto fun = [func](const CLI::results_t &res) { - T variable; - bool result = detail::lexical_conversion(res, variable); + ArgType variable; + bool result = detail::lexical_conversion(res, variable); if(result) { func(variable); } @@ -632,15 +650,15 @@ class App { }; Option *opt = add_option(option_name, std::move(fun), option_description, false); - opt->type_name(detail::type_name()); - opt->type_size(detail::type_count::value); - opt->expected(detail::expected_count::value); + opt->type_name(detail::type_name()); + opt->type_size(detail::type_count_min::value, detail::type_count::value); + opt->expected(detail::expected_count::value); return opt; } /// Add option with no description or variable assignment Option *add_option(std::string option_name) { - return add_option(option_name, CLI::callback_t(), std::string{}, false); + return add_option(option_name, CLI::callback_t{}, std::string{}, false); } /// Add option with description but with no variable assignment or callback @@ -749,7 +767,7 @@ class App { /// 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 && + enable_if_t::value && !std::is_const::value && (!std::is_integral::value || is_bool::value) && !std::is_constructible, T>::value, detail::enabler> = detail::dummy> @@ -873,7 +891,7 @@ class App { return opt; } - /// Add a complex number + /// Add a complex number DEPRECATED --use add_option instead template Option *add_complex(std::string option_name, T &variable, @@ -2652,7 +2670,12 @@ class App { // Get a reference to the pointer to make syntax bearable Option_p &op = *op_ptr; - + /// if we require a separator add it here + if(op->get_inject_separator()) { + if(!op->results().empty() && !op->results().back().empty()) { + op->add_result(std::string{}); + } + } int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min()); int max_num = op->get_items_expected_max(); @@ -2717,7 +2740,7 @@ class App { } // if we only partially completed a type then add an empty string for later processing - if(min_num > 0 && op->get_type_size_max() != min_num && collected % op->get_type_size_max() != 0) { + if(min_num > 0 && op->get_type_size_max() != min_num && (collected % op->get_type_size_max()) != 0) { op->add_result(std::string{}); } diff --git a/include/CLI/Option.hpp b/include/CLI/Option.hpp index 2e0293038..ae8907d5c 100644 --- a/include/CLI/Option.hpp +++ b/include/CLI/Option.hpp @@ -315,7 +315,7 @@ class Option : public OptionBase