Commit d81843002a8d53ffb49db4284cfd703ae659af1e

Authored by Henry Schreiner
Committed by GitHub
1 parent ca4bc6a6

To string and default option revamp (#242)

* First streaming version

* Using to_string instead

* Switching to new backend

* Moving to capture function for defaults

* Rename capture_default + _str

* defaultval -> default_str, added always_capture_default

* Fix style

* Adding tests and docs to readme

* Dropping macOS on Travis (supported through Azure)
.travis.yml
@@ -118,16 +118,6 @@ matrix: @@ -118,16 +118,6 @@ matrix:
118 script: 118 script:
119 - .ci/make_and_test.sh 11 119 - .ci/make_and_test.sh 11
120 120
121 - # macOS and clang  
122 - - os: osx  
123 - compiler: clang  
124 - install:  
125 - - brew update  
126 - - echo 'brew "python"' > Brewfile  
127 - - echo 'brew "ccache"' >> Brewfile  
128 - - brew bundle  
129 - - python -m ensurepip --user  
130 -  
131 install: skip 121 install: skip
132 122
133 script: 123 script:
CHANGELOG.md
1 ## Version 1.8: Sets and Flags (IN PROGRESS) 1 ## Version 1.8: Sets and Flags (IN PROGRESS)
2 2
3 -Set handling has been completely replaced by a new backend that works as a Validator. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. You can also use a collection of pairs (like `std::map`) to transform the match into an output. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support general flag types. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support. 3 +Set handling has been completely replaced by a new backend that works as a Validator. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. You can also use a collection of pairs (like `std::map`) to transform the match into an output. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support general flag types. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support. The final bool "defaults" flag when creating options has been replaced by `->capture_default_str()` (ending an old limitation in construction made this possible); the old method is still available but may be removed in future versions.
4 4
  5 +* Replaced default help capture: `.add_option("name", value, "", True)` becomes `.add_option("name", value)->capture_default_str()` [#242]
  6 +* Added `.always_capture_default()` [#242]
5 * New `CLI::IsMember` validator replaces set validation [#222] 7 * New `CLI::IsMember` validator replaces set validation [#222]
6 * IsMember also supports container of pairs, transform allows modification of result [#228] 8 * IsMember also supports container of pairs, transform allows modification of result [#228]
7 * Much more powerful flags with different values [#211], general types [#235] 9 * Much more powerful flags with different values [#211], general types [#235]
8 * `add_option` now supports bool due to unified bool handling [#211] 10 * `add_option` now supports bool due to unified bool handling [#211]
9 * Support for composable unnamed subcommands [#216] 11 * Support for composable unnamed subcommands [#216]
  12 +* Reparsing is better supported with `.remaining_for_passthrough()` [#265]
10 * Custom vector separator using `->delimiter(char)` [#209], [#221], [#240] 13 * Custom vector separator using `->delimiter(char)` [#209], [#221], [#240]
11 -* Validators added for IP4 addresses and positive numbers [#210] 14 +* Validators added for IP4 addresses and positive numbers [#210] and numbers [#262]
12 * Minimum required Boost for optional Optionals has been corrected to 1.61 [#226] 15 * Minimum required Boost for optional Optionals has been corrected to 1.61 [#226]
13 * Positionals can stop options from being parsed with `app.positionals_at_end()` [#223] 16 * Positionals can stop options from being parsed with `app.positionals_at_end()` [#223]
  17 +* Added `validate_positionals` [#262]
  18 +* Positional parsing is much more powerful [#251], duplicates supported []#247]
14 * Validators can be negated with `!` [#230], and now handle tname functions [#228] 19 * Validators can be negated with `!` [#230], and now handle tname functions [#228]
15 * Better enum support and streaming helper [#233] and [#228] 20 * Better enum support and streaming helper [#233] and [#228]
16 * Cleanup for shadow warnings [#232] 21 * Cleanup for shadow warnings [#232]
  22 +* Better alignment on multiline descriptions [#269]
  23 +* Better support for aarch64 [#266]
17 24
18 > ### Converting from CLI11 1.7: 25 > ### Converting from CLI11 1.7:
19 > 26 >
  27 +> * `.add_option(..., true)` should be replaced by `.add_option(...)->capture_default_str()` or `app.option_defaults()->always_capture_default()` can be used
20 > * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))` 28 > * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))`
21 > * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember` 29 > * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember`
22 > * The `_ignore_underscore` version of this can be replaced by adding `CLI::ignore_underscore` to the argument list in `IsMember` 30 > * The `_ignore_underscore` version of this can be replaced by adding `CLI::ignore_underscore` to the argument list in `IsMember`
@@ -39,6 +47,13 @@ Set handling has been completely replaced by a new backend that works as a Valid @@ -39,6 +47,13 @@ Set handling has been completely replaced by a new backend that works as a Valid
39 [#233]: https://github.com/CLIUtils/CLI11/pull/233 47 [#233]: https://github.com/CLIUtils/CLI11/pull/233
40 [#235]: https://github.com/CLIUtils/CLI11/pull/235 48 [#235]: https://github.com/CLIUtils/CLI11/pull/235
41 [#240]: https://github.com/CLIUtils/CLI11/pull/240 49 [#240]: https://github.com/CLIUtils/CLI11/pull/240
  50 +[#242]: https://github.com/CLIUtils/CLI11/pull/242
  51 +[#247]: https://github.com/CLIUtils/CLI11/pull/247
  52 +[#251]: https://github.com/CLIUtils/CLI11/pull/251
  53 +[#262]: https://github.com/CLIUtils/CLI11/pull/262
  54 +[#265]: https://github.com/CLIUtils/CLI11/pull/265
  55 +[#266]: https://github.com/CLIUtils/CLI11/pull/266
  56 +[#269]: https://github.com/CLIUtils/CLI11/pull/269
42 57
43 58
44 ## Version 1.7.1: Quick patch 59 ## Version 1.7.1: Quick patch
README.md
@@ -33,11 +33,11 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature @@ -33,11 +33,11 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature
33 - [Option types](#option-types) 33 - [Option types](#option-types)
34 - [Example](#example) 34 - [Example](#example)
35 - [Option options](#option-options) 35 - [Option options](#option-options)
36 - - [Validators](#validators) ๐Ÿšง  
37 - - [Transforming Validators](#transforming-validators)๐Ÿšง  
38 - - [Validator operations](#validator-operations)๐Ÿšง  
39 - - [Custom Validators](#custom-validators)๐Ÿšง  
40 - - [Querying Validators](#querying-validators)๐Ÿšง 36 + - [Validators](#validators) ๐Ÿšง
  37 + - [Transforming Validators](#transforming-validators) ๐Ÿšง
  38 + - [Validator operations](#validator-operations) ๐Ÿšง
  39 + - [Custom Validators](#custom-validators) ๐Ÿšง
  40 + - [Querying Validators](#querying-validators) ๐Ÿšง
41 - [Getting Results](#getting-results) ๐Ÿšง 41 - [Getting Results](#getting-results) ๐Ÿšง
42 - [Subcommands](#subcommands) 42 - [Subcommands](#subcommands)
43 - [Subcommand options](#subcommand-options) 43 - [Subcommand options](#subcommand-options)
@@ -64,7 +64,7 @@ CLI11 provides all the features you expect in a powerful command line parser, wi @@ -64,7 +64,7 @@ CLI11 provides all the features you expect in a powerful command line parser, wi
64 It is tested on [Travis][], [AppVeyor][], and [Azure][], and is being included in the [GooFit GPU fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for Python. CLI11 has a user friendly introduction in this README, a more in-depth tutorial [GitBook][], as well as [API documentation][api-docs] generated by Travis. 64 It is tested on [Travis][], [AppVeyor][], and [Azure][], and is being included in the [GooFit GPU fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for Python. CLI11 has a user friendly introduction in this README, a more in-depth tutorial [GitBook][], as well as [API documentation][api-docs] generated by Travis.
65 See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for current and past releases. Also see the [Version 1.0 post][], [Version 1.3 post][], or [Version 1.6 post][] for more information. 65 See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for current and past releases. Also see the [Version 1.0 post][], [Version 1.3 post][], or [Version 1.6 post][] for more information.
66 66
67 -You can be notified when new releases are made by subscribing to <https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly, or use the releases mode of the github watching tool. 67 +You can be notified when new releases are made by subscribing to <https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly, or use the releases mode of the github watching tool.
68 68
69 ### Why write another CLI parser? 69 ### Why write another CLI parser?
70 70
@@ -74,7 +74,7 @@ An acceptable CLI parser library should be all of the following: @@ -74,7 +74,7 @@ An acceptable CLI parser library should be all of the following:
74 - Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability. 74 - Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
75 - C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.5+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+. 75 - C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.5+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
76 - Work on Linux, macOS, and Windows. 76 - Work on Linux, macOS, and Windows.
77 -- Well tested using [Travis][] (Linux and macOS) and [AppVeyor][] (Windows) or [Azure][] (all three). "Well" is defined as having good coverage measured by [CodeCov][]. 77 +- Well tested using [Travis][] (Linux) and [AppVeyor][] (Windows) or [Azure][] (all three). "Well" is defined as having good coverage measured by [CodeCov][].
78 - Clear help printing. 78 - Clear help printing.
79 - Nice error messages. 79 - Nice error messages.
80 - Standard shell idioms supported naturally, like grouping flags, a positional separator, etc. 80 - Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
@@ -193,8 +193,7 @@ app.add_option(option_name, help_str=&quot;&quot;) // ๐Ÿšง @@ -193,8 +193,7 @@ app.add_option(option_name, help_str=&quot;&quot;) // ๐Ÿšง
193 193
194 app.add_option(option_name, 194 app.add_option(option_name,
195 variable_to_bind_to, // bool, int, float, vector, ๐Ÿšง enum, or string-like, or anything with a defined conversion from a string 195 variable_to_bind_to, // bool, int, float, vector, ๐Ÿšง enum, or string-like, or anything with a defined conversion from a string
196 - help_string="",  
197 - default=false) 196 + help_string="")
198 197
199 app.add_option_function<type>(option_name, 198 app.add_option_function<type>(option_name,
200 function <void(const type &value)>, // ๐Ÿšง int, bool, float, enum, vector, or string-like, or anything with a defined conversion from a string 199 function <void(const type &value)>, // ๐Ÿšง int, bool, float, enum, vector, or string-like, or anything with a defined conversion from a string
@@ -225,8 +224,7 @@ Option_group *app.add_option_group(name,description); // ๐Ÿšง @@ -225,8 +224,7 @@ Option_group *app.add_option_group(name,description); // ๐Ÿšง
225 -app.add_set(option_name, 224 -app.add_set(option_name,
226 - variable_to_bind_to, // Same type as stored by set 225 - variable_to_bind_to, // Same type as stored by set
227 - set_of_possible_options, // Set will be copied, ignores changes 226 - set_of_possible_options, // Set will be copied, ignores changes
228 -- help_string="",  
229 -- default=false) 227 +- help_string="")
230 -app.add_mutable_set(... // ๐Ÿ†• Set can change later, keeps reference 228 -app.add_mutable_set(... // ๐Ÿ†• Set can change later, keeps reference
231 -app.add_set_ignore_case(... // String only 229 -app.add_set_ignore_case(... // String only
232 -app.add_mutable_set_ignore_case(... // ๐Ÿ†• String only 230 -app.add_mutable_set_ignore_case(... // ๐Ÿ†• String only
@@ -236,9 +234,9 @@ Option_group *app.add_option_group(name,description); // ๐Ÿšง @@ -236,9 +234,9 @@ Option_group *app.add_option_group(name,description); // ๐Ÿšง
236 -app.add_mutable_set_ignore_case_underscore(... // ๐Ÿ†• String only 234 -app.add_mutable_set_ignore_case_underscore(... // ๐Ÿ†• String only
237 ``` 235 ```
238 236
239 -An option name must start with a alphabetic character, underscore, a number ๐Ÿšง, '?'๐Ÿšง, or '@'๐Ÿšง. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option`. 237 +An option name must start with a alphabetic character, underscore, a number ๐Ÿšง, '?' ๐Ÿšง, or '@' ๐Ÿšง. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form.
240 238
241 -The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed. The type can be any type supported by the `add_option` function. The function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` possibly) if the value is not valid. 239 +The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed. The type can be any type supported by the `add_option` function. The function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` possibly) if the value is not valid.
242 240
243 ๐Ÿšง Flag options specified through the `add_flag*` functions allow a syntax for the option names to default particular options to a false value or any other value if some flags are passed. For example: 241 ๐Ÿšง Flag options specified through the `add_flag*` functions allow a syntax for the option names to default particular options to a false value or any other value if some flags are passed. For example:
244 242
@@ -285,16 +283,19 @@ Before parsing, you can set the following options: @@ -285,16 +283,19 @@ Before parsing, you can set the following options:
285 - `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden). 283 - `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden).
286 - `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments). 284 - `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
287 - `->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 285 - `->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
288 -- `->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. 286 +- `->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.
289 - `->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. 287 - `->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.
290 - `->description(str)`: ๐Ÿ†• Set/change the description. 288 - `->description(str)`: ๐Ÿ†• Set/change the description.
291 - `->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). 289 - `->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).
292 -- `->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 290 +- `->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
293 - `->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. 291 - `->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.
294 - `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options. 292 - `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options.
295 - `->transform(Validator)`: uses a Validator object to do the transformation see [Validators](#validators) for a description of available Validators and how to create new ones. 293 - `->transform(Validator)`: uses a Validator object to do the transformation see [Validators](#validators) for a description of available Validators and how to create new ones.
296 - `->each(void(const std::string &)>`: Run this function on each value received, as it is received. It should throw a `ValidationError` if an error is encountered. 294 - `->each(void(const std::string &)>`: Run this function on each value received, as it is received. It should throw a `ValidationError` if an error is encountered.
297 - `->configurable(false)`: Disable this option from being in a configuration file. 295 - `->configurable(false)`: Disable this option from being in a configuration file.
  296 + `->capture_default_str()`: ๐Ÿšง Store the current value attached and display it in the help string.
  297 + `->default_function(std::string())`: ๐Ÿšง Advanced: Change the function that `capture_default_str()` uses.
  298 + `->always_capture_default()`: ๐Ÿšง Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`.
298 299
299 300
300 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<std::string>` of results. 301 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<std::string>` of results.
@@ -324,7 +325,7 @@ On the command line, options can be given as: @@ -324,7 +325,7 @@ On the command line, options can be given as:
324 325
325 Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments. 326 Extra positional arguments will cause the program to exit, so at least one positional option with a vector is recommended if you want to allow extraneous arguments.
326 If you set `.allow_extras()` on the main `App`, you will not get an error. You can access the missing options using `remaining` (if you have subcommands, `app.remaining(true)` will get all remaining options, subcommands included). 327 If you set `.allow_extras()` on the main `App`, you will not get an error. You can access the missing options using `remaining` (if you have subcommands, `app.remaining(true)` will get all remaining options, subcommands included).
327 -If the remaining arguments are to processed by another `App` then the function `remaining_for_passthrough()`๐Ÿšง can be used to get the remaining arguments in reverse order such that `app.parse(vector)` works directly and could even be used inside a subcommand callback. 328 +If the remaining arguments are to processed by another `App` then the function `remaining_for_passthrough()` ๐Ÿšง can be used to get the remaining arguments in reverse order such that `app.parse(vector)` works directly and could even be used inside a subcommand callback.
328 329
329 You can access a vector of pointers to the parsed options in the original order using `parse_order()`. 330 You can access a vector of pointers to the parsed options in the original order using `parse_order()`.
330 If `--` is present in the command line that does not end an unlimited option, then 331 If `--` is present in the command line that does not end an unlimited option, then
@@ -349,20 +350,25 @@ CLI11 has several Validators built-in that perform some common checks @@ -349,20 +350,25 @@ CLI11 has several Validators built-in that perform some common checks
349 - `CLI::ValidIPV4`: ๐Ÿšง Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`. 350 - `CLI::ValidIPV4`: ๐Ÿšง Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`.
350 351
351 These Validators can be used by simply passing the name into the `check` or `transform` methods on an option 352 These Validators can be used by simply passing the name into the `check` or `transform` methods on an option
  353 +
352 ```cpp 354 ```cpp
353 ->check(CLI::ExistingFile); 355 ->check(CLI::ExistingFile);
354 ->check(CLI::Range(0,10)); 356 ->check(CLI::Range(0,10));
355 ``` 357 ```
356 358
357 -Validators can be merged using `&` and `|` and inverted using `!`๐Ÿšง. For example 359 +Validators can be merged using `&` and `|` and inverted using `!` ๐Ÿšง. For example:
  360 +
358 ```cpp 361 ```cpp
359 ->check(CLI::Range(0,10)|CLI::Range(20,30)); 362 ->check(CLI::Range(0,10)|CLI::Range(20,30));
360 ``` 363 ```
  364 +
361 will produce a check to ensure a value is between 0 and 10 or 20 and 30. 365 will produce a check to ensure a value is between 0 and 10 or 20 and 30.
  366 +
362 ```cpp 367 ```cpp
363 ->check(!CLI::PositiveNumber); 368 ->check(!CLI::PositiveNumber);
364 ``` 369 ```
365 -will produce a check for a number less than 0; 370 +
  371 +will produce a check for a number less than 0.
366 372
367 ##### Transforming Validators 373 ##### Transforming Validators
368 There are a few built in Validators that let you transform values if used with the `transform` function. If they also do some checks then they can be used `check` but some may do nothing in that case. 374 There are a few built in Validators that let you transform values if used with the `transform` function. If they also do some checks then they can be used `check` but some may do nothing in that case.
@@ -377,7 +383,7 @@ of `IsMember`: @@ -377,7 +383,7 @@ of `IsMember`:
377 * `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`. 383 * `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
378 * `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key. The value member of the map is not used in `IsMember`, so it can be any type. 384 * `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key. The value member of the map is not used in `IsMember`, so it can be any type.
379 * `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later. 385 * `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
380 -* ๐Ÿšง The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map. `Transformer` does not do any checking so values not in the map are ignored. `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`. A Transformer placed using `check` will not do anything. 386 +* ๐Ÿšง The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map. `Transformer` does not do any checking so values not in the map are ignored. `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`. A Transformer placed using `check` will not do anything.
381 After specifying a map of options, you can also specify "filter" just like in `CLI::IsMember`. 387 After specifying a map of options, you can also specify "filter" just like in `CLI::IsMember`.
382 Here are some examples (`Transformer` and `CheckedTransformer` are interchangeable in the examples) 388 Here are some examples (`Transformer` and `CheckedTransformer` are interchangeable in the examples)
383 of `Transformer`: 389 of `Transformer`:
@@ -385,12 +391,12 @@ of `Transformer`: @@ -385,12 +391,12 @@ of `Transformer`:
385 * `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values. 391 * `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
386 392
387 * `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work, the `::value_type` needs to produce a pair of some kind. 393 * `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work, the `::value_type` needs to produce a pair of some kind.
388 - * `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value. `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs. 394 + * `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value. `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs.
389 * `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>` 395 * `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>`
390 396
391 NOTES: If the container used in `IsMember`, `Transformer`, or `CheckedTransformer` has a `find` function like `std::unordered_map` or `std::map` then that function is used to do the searching. If it does not have a `find` function a linear search is performed. If there are filters present, the fast search is performed first, and if that fails a linear search with the filters on the key values is performed. 397 NOTES: If the container used in `IsMember`, `Transformer`, or `CheckedTransformer` has a `find` function like `std::unordered_map` or `std::map` then that function is used to do the searching. If it does not have a `find` function a linear search is performed. If there are filters present, the fast search is performed first, and if that fails a linear search with the filters on the key values is performed.
392 398
393 -##### Validator operations๐Ÿšง 399 +##### Validator operations ๐Ÿšง
394 Validators are copyable and have a few operations that can be performed on them to alter settings. Most of the built in Validators have a default description that is displayed in the help. This can be altered via `.description(validator_description)`. 400 Validators are copyable and have a few operations that can be performed on them to alter settings. Most of the built in Validators have a default description that is displayed in the help. This can be altered via `.description(validator_description)`.
395 The name of a Validator, which is useful for later reference from the `get_validator(name)` method of an `Option` can be set via `.name(validator_name)` 401 The name of a Validator, which is useful for later reference from the `get_validator(name)` method of an `Option` can be set via `.name(validator_name)`
396 The operation function of a Validator can be set via 402 The operation function of a Validator can be set via
@@ -406,7 +412,7 @@ The check can later be activated through @@ -406,7 +412,7 @@ The check can later be activated through
406 opt->get_validator("range")->active(); 412 opt->get_validator("range")->active();
407 ``` 413 ```
408 414
409 -##### Custom Validators๐Ÿšง 415 +##### Custom Validators ๐Ÿšง
410 416
411 A validator object with a custom function can be created via 417 A validator object with a custom function can be created via
412 ```cpp 418 ```cpp
@@ -427,10 +433,10 @@ opt-&gt;get_validator(name); @@ -427,10 +433,10 @@ opt-&gt;get_validator(name);
427 This will retrieve a Validator with the given name or throw a `CLI::OptionNotFound` error. If no name is given or name is empty the first unnamed Validator will be returned or the first Validator if there is only one. 433 This will retrieve a Validator with the given name or throw a `CLI::OptionNotFound` error. If no name is given or name is empty the first unnamed Validator will be returned or the first Validator if there is only one.
428 434
429 Validators have a few functions to query the current values 435 Validators have a few functions to query the current values
430 - * `get_description()`:๐Ÿšง Will return a description string  
431 - * `get_name()`:๐Ÿšง Will return the Validator name  
432 - * `get_active()`:๐Ÿšง Will return the current active state, true if the Validator is active.  
433 - * `get_modifying()`: ๐Ÿšง Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()`๐Ÿšง method, though it is recommended to let `check` and `transform` option methods manipulate it if needed. 436 + * `get_description()`: ๐Ÿšง Will return a description string
  437 + * `get_name()`: ๐Ÿšง Will return the Validator name
  438 + * `get_active()`: ๐Ÿšง Will return the current active state, true if the Validator is active.
  439 + * `get_modifying()`: ๐Ÿšง Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()` ๐Ÿšง method, though it is recommended to let `check` and `transform` option methods manipulate it if needed.
434 440
435 #### Getting results 441 #### Getting results
436 In most cases, the fastest and easiest way is to return the results through a callback or variable specified in one of the `add_*` functions. But there are situations where this is not possible or desired. For these cases the results may be obtained through one of the following functions. Please note that these functions will do any type conversions and processing during the call so should not used in performance critical code: 442 In most cases, the fastest and easiest way is to return the results through a callback or variable specified in one of the `add_*` functions. But there are situations where this is not possible or desired. For these cases the results may be obtained through one of the following functions. Please note that these functions will do any type conversions and processing during the call so should not used in performance critical code:
@@ -456,7 +462,7 @@ You are allowed to throw `CLI::Success` in the callbacks. @@ -456,7 +462,7 @@ You are allowed to throw `CLI::Success` in the callbacks.
456 Multiple subcommands are allowed, to allow [`Click`][click] like series of commands (order is preserved). The same subcommand can be triggered multiple times but all positional arguments will take precedence over the second and future calls of the subcommand. `->count()` on the subcommand will return the number of times the subcommand was called. The subcommand callback will only be triggered once unless the `.immediate_callback()` flag is set. In which case the callback executes on completion of the subcommand arguments but after the arguments for that subcommand have been parsed, and can be triggered multiple times. 462 Multiple subcommands are allowed, to allow [`Click`][click] like series of commands (order is preserved). The same subcommand can be triggered multiple times but all positional arguments will take precedence over the second and future calls of the subcommand. `->count()` on the subcommand will return the number of times the subcommand was called. The subcommand callback will only be triggered once unless the `.immediate_callback()` flag is set. In which case the callback executes on completion of the subcommand arguments but after the arguments for that subcommand have been parsed, and can be triggered multiple times.
457 463
458 ๐Ÿšง Subcommands may also have an empty name either by calling `add_subcommand` with an empty string for the name or with no arguments. 464 ๐Ÿšง Subcommands may also have an empty name either by calling `add_subcommand` with an empty string for the name or with no arguments.
459 -Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work. If an option is not defined in the main App, all nameless subcommands are checked as well. This allows for the options to be defined in a composable group. The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`. Multiple nameless subcommands are allowed. Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed. 465 +Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work. If an option is not defined in the main App, all nameless subcommands are checked as well. This allows for the options to be defined in a composable group. The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`. Multiple nameless subcommands are allowed. Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed.
460 466
461 #### Subcommand options 467 #### Subcommand options
462 468
@@ -467,22 +473,22 @@ There are several options that are supported on the main app and subcommands and @@ -467,22 +473,22 @@ There are several options that are supported on the main app and subcommands and
467 - `.allow_windows_style_options()`: ๐Ÿ†• Allow command line options to be parsed in the form of `/s /long /file:file_name.ext` This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`. 473 - `.allow_windows_style_options()`: ๐Ÿ†• Allow command line options to be parsed in the form of `/s /long /file:file_name.ext` This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`.
468 - `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through. 474 - `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through.
469 - `.disable()`: ๐Ÿšง Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group. 475 - `.disable()`: ๐Ÿšง Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group.
470 -- `.disabled_by_default()`:๐Ÿšง Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others. 476 +- `.disabled_by_default()`: ๐Ÿšง Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others.
471 - `.enabled_by_default()`: ๐Ÿšง Specify that at the start of each parse the subcommand/option_group should be enabled. This is useful for allowing some Subcommands to disable others. 477 - `.enabled_by_default()`: ๐Ÿšง Specify that at the start of each parse the subcommand/option_group should be enabled. This is useful for allowing some Subcommands to disable others.
472 -- `.validate_positionals()`:๐Ÿšง Specify that positionals should pass validation before matching. Validation is specified through `transform`, `check`, and `each` for an option. If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.  
473 -- `.excludes(option_or_subcommand)`: ๐Ÿšง If given an option pointer or pointer to another subcommand, these subcommands cannot be given together. In the case of options, if the option is passed the subcommand cannot be used and will generate an error. 478 +- `.validate_positionals()`: ๐Ÿšง Specify that positionals should pass validation before matching. Validation is specified through `transform`, `check`, and `each` for an option. If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.
  479 +- `.excludes(option_or_subcommand)`: ๐Ÿšง If given an option pointer or pointer to another subcommand, these subcommands cannot be given together. In the case of options, if the option is passed the subcommand cannot be used and will generate an error.
474 - `.require_option()`: ๐Ÿšง Require 1 or more options or option groups be used. 480 - `.require_option()`: ๐Ÿšง Require 1 or more options or option groups be used.
475 -- `.require_option(N)`: ๐Ÿšง Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more. 481 +- `.require_option(N)`: ๐Ÿšง Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
476 - `.require_option(min, max)`: ๐Ÿšง Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options. 482 - `.require_option(min, max)`: ๐Ÿšง Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
477 - `.require_subcommand()`: Require 1 or more subcommands. 483 - `.require_subcommand()`: Require 1 or more subcommands.
478 - `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more. 484 - `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
479 - `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited. 485 - `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
480 - `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand. 486 - `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand.
481 - `.add_subcommand(shared_ptr<App>)`: ๐Ÿšง Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand. 487 - `.add_subcommand(shared_ptr<App>)`: ๐Ÿšง Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand.
482 -- `.remove_subcommand(App)`:๐Ÿšง Remove a subcommand from the app or subcommand. 488 +- `.remove_subcommand(App)`: ๐Ÿšง Remove a subcommand from the app or subcommand.
483 - `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line. 489 - `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
484 - `.get_subcommands(filter)`: The list of subcommands that match a particular filter function. 490 - `.get_subcommands(filter)`: The list of subcommands that match a particular filter function.
485 -- `.add_option_group(name="", description="")`: ๐Ÿšง Add an [option group](#option-groups) to an App, an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact. 491 +- `.add_option_group(name="", description="")`: ๐Ÿšง Add an [option group](#option-groups) to an App, an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact.
486 - `.get_parent()`: Get the parent App or `nullptr` if called on master App. 492 - `.get_parent()`: Get the parent App or `nullptr` if called on master App.
487 - `.get_option(name)`: Get an option pointer by option name will throw if the specified option is not available, nameless subcommands are also searched 493 - `.get_option(name)`: Get an option pointer by option name will throw if the specified option is not available, nameless subcommands are also searched
488 - `.get_option_no_throw(name)`: ๐Ÿšง Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available. 494 - `.get_option_no_throw(name)`: ๐Ÿšง Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available.
@@ -514,7 +520,7 @@ There are several options that are supported on the main app and subcommands and @@ -514,7 +520,7 @@ There are several options that are supported on the main app and subcommands and
514 520
515 #### Callbacks 521 #### Callbacks
516 A subcommand has two optional callbacks that are executed at different stages of processing. The `preparse_callback` ๐Ÿšง is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process. For the main app the first argument is considered the program name, for subcommands the first argument is the subcommand name. For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group. 522 A subcommand has two optional callbacks that are executed at different stages of processing. The `preparse_callback` ๐Ÿšง is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process. For the main app the first argument is considered the program name, for subcommands the first argument is the subcommand name. For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
517 -The second callback is executed after parsing. The behavior depends on the status of the `immediate_callback` flag ๐Ÿšง. If true, this runs immediately after the parsing of the subcommand. Or if the flag is false, once after parsing of all arguments. If the `immediate_callback` is set then the callback can be executed multiple times if the subcommand list given multiple times. If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback. `immediate_callback()` has no effect on the main app, though it can be inherited. For option_groups `immediate_callback` causes the callback to be run prior to other option groups and options in the main app, effectively giving the options in the group priority. 523 +The second callback is executed after parsing. The behavior depends on the status of the `immediate_callback` flag ๐Ÿšง. If true, this runs immediately after the parsing of the subcommand. Or if the flag is false, once after parsing of all arguments. If the `immediate_callback` is set then the callback can be executed multiple times if the subcommand list given multiple times. If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback. `immediate_callback()` has no effect on the main app, though it can be inherited. For option_groups `immediate_callback` causes the callback to be run prior to other option groups and options in the main app, effectively giving the options in the group priority.
518 524
519 For example say an application was set up like 525 For example say an application was set up like
520 526
@@ -548,7 +554,7 @@ A subcommand is considered terminated when one of the following conditions are m @@ -548,7 +554,7 @@ A subcommand is considered terminated when one of the following conditions are m
548 3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand. 554 3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand.
549 4. The subcommand_terminator mark(`++`) is encountered 555 4. The subcommand_terminator mark(`++`) is encountered
550 556
551 -If the `immediate_callback` flag is set then all contained options are processed and the callback is triggered. If a subcommand with an `immediate_callback` flag is called again, then the contained options are reset, and can be triggered again. 557 +If the `immediate_callback` flag is set then all contained options are processed and the callback is triggered. If a subcommand with an `immediate_callback` flag is called again, then the contained options are reset, and can be triggered again.
552 558
553 559
554 560
@@ -579,7 +585,7 @@ This results in the subcommand being moved from its parent into the option group @@ -579,7 +585,7 @@ This results in the subcommand being moved from its parent into the option group
579 Options in an option group are searched for a command line match after any options in the main app, so any positionals in the main app would be matched first. So care must be taken to make sure of the order when using positional arguments and option groups. 585 Options in an option group are searched for a command line match after any options in the main app, so any positionals in the main app would be matched first. So care must be taken to make sure of the order when using positional arguments and option groups.
580 Option groups work well with `excludes` and `require_options` methods, as an Application will treat an option group as a single option for the purpose of counting and requirements, and an option group will be considered used if any of the options or subcommands contained in it are used. Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well. Disabling an option group will turn off all options within the group. 586 Option groups work well with `excludes` and `require_options` methods, as an Application will treat an option group as a single option for the purpose of counting and requirements, and an option group will be considered used if any of the options or subcommands contained in it are used. Option groups allow specifying requirements such as requiring 1 of 3 options in one group and 1 of 3 options in a different group. Option groups can contain other groups as well. Disabling an option group will turn off all options within the group.
581 587
582 -The `CLI::TriggerOn`๐Ÿšง and `CLI::TriggerOff`๐Ÿšง methods are helper methods to allow the use of options/subcommands from one group to trigger another group on or off. 588 +The `CLI::TriggerOn` ๐Ÿšง and `CLI::TriggerOff` ๐Ÿšง methods are helper methods to allow the use of options/subcommands from one group to trigger another group on or off.
583 589
584 ```cpp 590 ```cpp
585 CLI::TriggerOn(group1_pointer, triggered_group); 591 CLI::TriggerOn(group1_pointer, triggered_group);
examples/json.cpp
@@ -28,8 +28,8 @@ class ConfigJSON : public CLI::Config { @@ -28,8 +28,8 @@ class ConfigJSON : public CLI::Config {
28 j[name] = opt->results(); 28 j[name] = opt->results();
29 29
30 // If the option has a default and is requested by optional argument 30 // If the option has a default and is requested by optional argument
31 - else if(default_also && !opt->get_defaultval().empty())  
32 - j[name] = opt->get_defaultval(); 31 + else if(default_also && !opt->get_default_str().empty())
  32 + j[name] = opt->get_default_str();
33 33
34 // Flag, one passed 34 // Flag, one passed
35 } else if(opt->count() == 1) { 35 } else if(opt->count() == 1) {
include/CLI/App.hpp
@@ -437,17 +437,33 @@ class App { @@ -437,17 +437,33 @@ class App {
437 Option *add_option(std::string option_name, 437 Option *add_option(std::string option_name,
438 callback_t option_callback, 438 callback_t option_callback,
439 std::string option_description = "", 439 std::string option_description = "",
440 - bool defaulted = false) {  
441 - Option myopt{option_name, option_description, option_callback, defaulted, this}; 440 + bool defaulted = false,
  441 + std::function<std::string()> func = {}) {
  442 + Option myopt{option_name, option_description, option_callback, this};
442 443
443 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { 444 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
444 return *v == myopt; 445 return *v == myopt;
445 }) == std::end(options_)) { 446 }) == std::end(options_)) {
446 options_.emplace_back(); 447 options_.emplace_back();
447 Option_p &option = options_.back(); 448 Option_p &option = options_.back();
448 - option.reset(new Option(option_name, option_description, option_callback, defaulted, this)); 449 + option.reset(new Option(option_name, option_description, option_callback, this));
  450 +
  451 + // Set the default string capture function
  452 + option->default_function(func);
  453 +
  454 + // For compatibility with CLI11 1.7 and before, capture the default string here
  455 + if(defaulted)
  456 + option->capture_default_str();
  457 +
  458 + // Transfer defaults to the new option
449 option_defaults_.copy_to(option.get()); 459 option_defaults_.copy_to(option.get());
  460 +
  461 + // Don't bother to capture if we already did
  462 + if(!defaulted && option->get_always_capture_default())
  463 + option->capture_default_str();
  464 +
450 return option.get(); 465 return option.get();
  466 +
451 } else 467 } else
452 throw OptionAlreadyAdded(myopt.get_name()); 468 throw OptionAlreadyAdded(myopt.get_name());
453 } 469 }
@@ -456,12 +472,16 @@ class App { @@ -456,12 +472,16 @@ class App {
456 template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy> 472 template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy>
457 Option *add_option(std::string option_name, 473 Option *add_option(std::string option_name,
458 T &variable, ///< The variable to set 474 T &variable, ///< The variable to set
459 - std::string option_description = "") { 475 + std::string option_description = "",
  476 + bool defaulted = false) {
460 477
461 - CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); }; 478 + auto fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); };
462 479
463 - Option *opt = add_option(option_name, fun, option_description, false); 480 + Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
  481 + return std::string(CLI::detail::to_string(variable));
  482 + });
464 opt->type_name(detail::type_name<T>()); 483 opt->type_name(detail::type_name<T>());
  484 +
465 return opt; 485 return opt;
466 } 486 }
467 487
@@ -471,7 +491,7 @@ class App { @@ -471,7 +491,7 @@ class App {
471 const std::function<void(const T &)> &func, ///< the callback to execute 491 const std::function<void(const T &)> &func, ///< the callback to execute
472 std::string option_description = "") { 492 std::string option_description = "") {
473 493
474 - CLI::callback_t fun = [func](CLI::results_t res) { 494 + auto fun = [func](CLI::results_t res) {
475 T variable; 495 T variable;
476 bool result = detail::lexical_cast(res[0], variable); 496 bool result = detail::lexical_cast(res[0], variable);
477 if(result) { 497 if(result) {
@@ -484,6 +504,7 @@ class App { @@ -484,6 +504,7 @@ class App {
484 opt->type_name(detail::type_name<T>()); 504 opt->type_name(detail::type_name<T>());
485 return opt; 505 return opt;
486 } 506 }
  507 +
487 /// Add option with no description or variable assignment 508 /// Add option with no description or variable assignment
488 Option *add_option(std::string option_name) { 509 Option *add_option(std::string option_name) {
489 return add_option(option_name, CLI::callback_t(), std::string{}, false); 510 return add_option(option_name, CLI::callback_t(), std::string{}, false);
@@ -497,34 +518,14 @@ class App { @@ -497,34 +518,14 @@ class App {
497 return add_option(option_name, CLI::callback_t(), option_description, false); 518 return add_option(option_name, CLI::callback_t(), option_description, false);
498 } 519 }
499 520
500 - /// Add option for non-vectors with a default print, allow template to specify conversion type  
501 - template <typename T,  
502 - typename XC = T,  
503 - enable_if_t<!is_vector<XC>::value && !std::is_const<XC>::value, detail::enabler> = detail::dummy>  
504 - Option *add_option(std::string option_name,  
505 - T &variable, ///< The variable to set  
506 - std::string option_description,  
507 - bool defaulted) {  
508 - static_assert(std::is_constructible<T, XC>::value, "assign type must be assignable from conversion type");  
509 - CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast<XC>(res[0], variable); };  
510 -  
511 - Option *opt = add_option(option_name, fun, option_description, defaulted);  
512 - opt->type_name(detail::type_name<XC>());  
513 - if(defaulted) {  
514 - std::stringstream out;  
515 - out << variable;  
516 - opt->default_str(out.str());  
517 - }  
518 - return opt;  
519 - }  
520 -  
521 /// Add option for vectors 521 /// Add option for vectors
522 template <typename T> 522 template <typename T>
523 Option *add_option(std::string option_name, 523 Option *add_option(std::string option_name,
524 std::vector<T> &variable, ///< The variable vector to set 524 std::vector<T> &variable, ///< The variable vector to set
525 - std::string option_description = "") { 525 + std::string option_description = "",
  526 + bool defaulted = false) {
526 527
527 - CLI::callback_t fun = [&variable](CLI::results_t res) { 528 + auto fun = [&variable](CLI::results_t res) {
528 bool retval = true; 529 bool retval = true;
529 variable.clear(); 530 variable.clear();
530 variable.reserve(res.size()); 531 variable.reserve(res.size());
@@ -536,35 +537,18 @@ class App { @@ -536,35 +537,18 @@ class App {
536 return (!variable.empty()) && retval; 537 return (!variable.empty()) && retval;
537 }; 538 };
538 539
539 - Option *opt = add_option(option_name, fun, option_description, false);  
540 - opt->type_name(detail::type_name<T>())->type_size(-1);  
541 - return opt;  
542 - }  
543 -  
544 - /// Add option for vectors with defaulted argument  
545 - template <typename T>  
546 - Option *add_option(std::string option_name,  
547 - std::vector<T> &variable, ///< The variable vector to set  
548 - std::string option_description,  
549 - bool defaulted) {  
550 -  
551 - CLI::callback_t fun = [&variable](CLI::results_t res) {  
552 - bool retval = true;  
553 - variable.clear();  
554 - variable.reserve(res.size());  
555 - for(const auto &elem : res) {  
556 -  
557 - variable.emplace_back();  
558 - retval &= detail::lexical_cast(elem, variable.back());  
559 - }  
560 - return (!variable.empty()) && retval; 540 + auto default_function = [&variable]() {
  541 + std::vector<std::string> defaults;
  542 + defaults.resize(variable.size());
  543 + std::transform(variable.begin(), variable.end(), defaults.begin(), [](T &val) {
  544 + return std::string(CLI::detail::to_string(val));
  545 + });
  546 + return std::string("[" + detail::join(defaults) + "]");
561 }; 547 };
562 548
563 - Option *opt = add_option(option_name, fun, option_description, defaulted); 549 + Option *opt = add_option(option_name, fun, option_description, defaulted, default_function);
564 opt->type_name(detail::type_name<T>())->type_size(-1); 550 opt->type_name(detail::type_name<T>())->type_size(-1);
565 551
566 - if(defaulted)  
567 - opt->default_str("[" + detail::join(variable) + "]");  
568 return opt; 552 return opt;
569 } 553 }
570 554
@@ -995,13 +979,16 @@ class App { @@ -995,13 +979,16 @@ class App {
995 return worked; 979 return worked;
996 }; 980 };
997 981
998 - CLI::Option *opt = add_option(option_name, std::move(fun), std::move(option_description), defaulted);  
999 - opt->type_name(label)->type_size(2);  
1000 - if(defaulted) { 982 + auto default_function = [&variable]() {
1001 std::stringstream out; 983 std::stringstream out;
1002 out << variable; 984 out << variable;
1003 - opt->default_str(out.str());  
1004 - } 985 + return out.str();
  986 + };
  987 +
  988 + CLI::Option *opt =
  989 + add_option(option_name, std::move(fun), std::move(option_description), defaulted, default_function);
  990 +
  991 + opt->type_name(label)->type_size(2);
1005 return opt; 992 return opt;
1006 } 993 }
1007 994
include/CLI/Config.hpp
@@ -32,8 +32,8 @@ ConfigINI::to_config(const App *app, bool default_also, bool write_description, @@ -32,8 +32,8 @@ ConfigINI::to_config(const App *app, bool default_also, bool write_description,
32 value = detail::ini_join(opt->results()); 32 value = detail::ini_join(opt->results());
33 33
34 // If the option has a default and is requested by optional argument 34 // If the option has a default and is requested by optional argument
35 - else if(default_also && !opt->get_defaultval().empty())  
36 - value = opt->get_defaultval(); 35 + else if(default_also && !opt->get_default_str().empty())
  36 + value = opt->get_default_str();
37 // Flag, one passed 37 // Flag, one passed
38 } else if(opt->count() == 1) { 38 } else if(opt->count() == 1) {
39 value = "true"; 39 value = "true";
include/CLI/Formatter.hpp
@@ -232,8 +232,8 @@ inline std::string Formatter::make_option_opts(const Option *opt) const { @@ -232,8 +232,8 @@ inline std::string Formatter::make_option_opts(const Option *opt) const {
232 if(opt->get_type_size() != 0) { 232 if(opt->get_type_size() != 0) {
233 if(!opt->get_type_name().empty()) 233 if(!opt->get_type_name().empty())
234 out << " " << get_label(opt->get_type_name()); 234 out << " " << get_label(opt->get_type_name());
235 - if(!opt->get_defaultval().empty())  
236 - out << "=" << opt->get_defaultval(); 235 + if(!opt->get_default_str().empty())
  236 + out << "=" << opt->get_default_str();
237 if(opt->get_expected() > 1) 237 if(opt->get_expected() > 1)
238 out << " x " << opt->get_expected(); 238 out << " x " << opt->get_expected();
239 if(opt->get_expected() == -1) 239 if(opt->get_expected() == -1)
include/CLI/Option.hpp
@@ -50,10 +50,16 @@ template &lt;typename CRTP&gt; class OptionBase { @@ -50,10 +50,16 @@ template &lt;typename CRTP&gt; class OptionBase {
50 50
51 /// Allow this option to be given in a configuration file 51 /// Allow this option to be given in a configuration file
52 bool configurable_{true}; 52 bool configurable_{true};
  53 +
53 /// Disable overriding flag values with '=value' 54 /// Disable overriding flag values with '=value'
54 bool disable_flag_override_{false}; 55 bool disable_flag_override_{false};
  56 +
55 /// Specify a delimiter character for vector arguments 57 /// Specify a delimiter character for vector arguments
56 char delimiter_{'\0'}; 58 char delimiter_{'\0'};
  59 +
  60 + /// Automatically capture default value
  61 + bool always_capture_default_{false};
  62 +
57 /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too) 63 /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too)
58 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw}; 64 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
59 65
@@ -66,6 +72,7 @@ template &lt;typename CRTP&gt; class OptionBase { @@ -66,6 +72,7 @@ template &lt;typename CRTP&gt; class OptionBase {
66 other->configurable(configurable_); 72 other->configurable(configurable_);
67 other->disable_flag_override(disable_flag_override_); 73 other->disable_flag_override(disable_flag_override_);
68 other->delimiter(delimiter_); 74 other->delimiter(delimiter_);
  75 + other->always_capture_default(always_capture_default_);
69 other->multi_option_policy(multi_option_policy_); 76 other->multi_option_policy(multi_option_policy_);
70 } 77 }
71 78
@@ -87,6 +94,11 @@ template &lt;typename CRTP&gt; class OptionBase { @@ -87,6 +94,11 @@ template &lt;typename CRTP&gt; class OptionBase {
87 /// Support Plumbum term 94 /// Support Plumbum term
88 CRTP *mandatory(bool value = true) { return required(value); } 95 CRTP *mandatory(bool value = true) { return required(value); }
89 96
  97 + CRTP *always_capture_default(bool value = true) {
  98 + always_capture_default_ = value;
  99 + return static_cast<CRTP *>(this);
  100 + }
  101 +
90 // Getters 102 // Getters
91 103
92 /// Get the group of this option 104 /// Get the group of this option
@@ -107,7 +119,12 @@ template &lt;typename CRTP&gt; class OptionBase { @@ -107,7 +119,12 @@ template &lt;typename CRTP&gt; class OptionBase {
107 /// The status of configurable 119 /// The status of configurable
108 bool get_disable_flag_override() const { return disable_flag_override_; } 120 bool get_disable_flag_override() const { return disable_flag_override_; }
109 121
  122 + /// Get the current delimeter char
110 char get_delimiter() const { return delimiter_; } 123 char get_delimiter() const { return delimiter_; }
  124 +
  125 + /// Return true if this will automatically capture the default value for help printing
  126 + bool get_always_capture_default() const { return always_capture_default_; }
  127 +
111 /// The status of the multi option policy 128 /// The status of the multi option policy
112 MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; } 129 MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
113 130
@@ -219,16 +236,16 @@ class Option : public OptionBase&lt;Option&gt; { @@ -219,16 +236,16 @@ class Option : public OptionBase&lt;Option&gt; {
219 /// The description for help strings 236 /// The description for help strings
220 std::string description_; 237 std::string description_;
221 238
222 - /// A human readable default value, usually only set if default is true in creation  
223 - std::string defaultval_; 239 + /// A human readable default value, either manually set, captured, or captured by default
  240 + std::string default_str_;
224 241
225 /// A human readable type value, set when App creates this 242 /// A human readable type value, set when App creates this
226 /// 243 ///
227 /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents. 244 /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents.
228 std::function<std::string()> type_name_{[]() { return std::string(); }}; 245 std::function<std::string()> type_name_{[]() { return std::string(); }};
229 246
230 - /// True if this option has a default  
231 - bool default_{false}; 247 + /// Run this function to capture a default (ignore if empty)
  248 + std::function<std::string()> default_function_;
232 249
233 ///@} 250 ///@}
234 /// @name Configuration 251 /// @name Configuration
@@ -277,10 +294,8 @@ class Option : public OptionBase&lt;Option&gt; { @@ -277,10 +294,8 @@ class Option : public OptionBase&lt;Option&gt; {
277 Option(std::string option_name, 294 Option(std::string option_name,
278 std::string option_description, 295 std::string option_description,
279 std::function<bool(results_t)> callback, 296 std::function<bool(results_t)> callback,
280 - bool defaulted,  
281 App *parent) 297 App *parent)
282 - : description_(std::move(option_description)), default_(defaulted), parent_(parent),  
283 - callback_(std::move(callback)) { 298 + : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
284 std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name)); 299 std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
285 } 300 }
286 301
@@ -306,6 +321,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -306,6 +321,7 @@ class Option : public OptionBase&lt;Option&gt; {
306 321
307 /// Set the number of expected arguments (Flags don't use this) 322 /// Set the number of expected arguments (Flags don't use this)
308 Option *expected(int value) { 323 Option *expected(int value) {
  324 +
309 // Break if this is a flag 325 // Break if this is a flag
310 if(type_size_ == 0) 326 if(type_size_ == 0)
311 throw IncorrectConstruction::SetFlag(get_name(true, true)); 327 throw IncorrectConstruction::SetFlag(get_name(true, true));
@@ -532,8 +548,12 @@ class Option : public OptionBase&lt;Option&gt; { @@ -532,8 +548,12 @@ class Option : public OptionBase&lt;Option&gt; {
532 /// The set of options excluded 548 /// The set of options excluded
533 std::set<Option *> get_excludes() const { return excludes_; } 549 std::set<Option *> get_excludes() const { return excludes_; }
534 550
  551 + /// The default value (for help printing) DEPRECATED Use get_default_str() instead
  552 + CLI11_DEPRECATED("Use get_default_str() instead")
  553 + std::string get_defaultval() const { return default_str_; }
  554 +
535 /// The default value (for help printing) 555 /// The default value (for help printing)
536 - std::string get_defaultval() const { return defaultval_; } 556 + std::string get_default_str() const { return default_str_; }
537 557
538 /// Get the callback function 558 /// Get the callback function
539 callback_t get_callback() const { return callback_; } 559 callback_t get_callback() const { return callback_; }
@@ -571,9 +591,6 @@ class Option : public OptionBase&lt;Option&gt; { @@ -571,9 +591,6 @@ class Option : public OptionBase&lt;Option&gt; {
571 ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1)); 591 ((multi_option_policy_ != MultiOptionPolicy::Throw || (expected_ < 0 && type_size_ < 0) ? -1 : 1));
572 } 592 }
573 593
574 - /// True if this has a default value  
575 - int get_default() const { return default_; }  
576 -  
577 /// True if the argument can be given directly 594 /// True if the argument can be given directly
578 bool get_positional() const { return pname_.length() > 0; } 595 bool get_positional() const { return pname_.length() > 0; }
579 596
@@ -843,7 +860,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -843,7 +860,7 @@ class Option : public OptionBase&lt;Option&gt; {
843 void results(T &output) const { 860 void results(T &output) const {
844 bool retval; 861 bool retval;
845 if(results_.empty()) { 862 if(results_.empty()) {
846 - retval = detail::lexical_cast(defaultval_, output); 863 + retval = detail::lexical_cast(default_str_, output);
847 } else if(results_.size() == 1) { 864 } else if(results_.size() == 1) {
848 retval = detail::lexical_cast(results_[0], output); 865 retval = detail::lexical_cast(results_[0], output);
849 } else { 866 } else {
@@ -917,13 +934,27 @@ class Option : public OptionBase&lt;Option&gt; { @@ -917,13 +934,27 @@ class Option : public OptionBase&lt;Option&gt; {
917 return this; 934 return this;
918 } 935 }
919 936
920 - /// Set the default value string representation 937 + /// Set a capture function for the default. Mostly used by App.
  938 + Option *default_function(const std::function<std::string()> &func) {
  939 + default_function_ = func;
  940 + return this;
  941 + }
  942 +
  943 + /// Capture the default value from the original value (if it can be captured)
  944 + Option *capture_default_str() {
  945 + if(default_function_) {
  946 + default_str_ = default_function_();
  947 + }
  948 + return this;
  949 + }
  950 +
  951 + /// Set the default value string representation (does not change the contained value)
921 Option *default_str(std::string val) { 952 Option *default_str(std::string val) {
922 - defaultval_ = val; 953 + default_str_ = val;
923 return this; 954 return this;
924 } 955 }
925 956
926 - /// Set the default value string representation and evaluate 957 + /// Set the default value string representation and evaluate into the bound value
927 Option *default_val(std::string val) { 958 Option *default_val(std::string val) {
928 default_str(val); 959 default_str(val);
929 auto old_results = results_; 960 auto old_results = results_;
include/CLI/Optional.hpp
@@ -46,6 +46,7 @@ @@ -46,6 +46,7 @@
46 #endif 46 #endif
47 #if CLI11_BOOST_OPTIONAL 47 #if CLI11_BOOST_OPTIONAL
48 #include <boost/optional.hpp> 48 #include <boost/optional.hpp>
  49 +#include <boost/optional/optional_io.hpp>
49 #endif 50 #endif
50 // [CLI11:verbatim] 51 // [CLI11:verbatim]
51 52
include/CLI/TypeTools.hpp
@@ -124,6 +124,43 @@ struct pair_adaptor&lt; @@ -124,6 +124,43 @@ struct pair_adaptor&lt;
124 } 124 }
125 }; 125 };
126 126
  127 +// Check for streamability
  128 +// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
  129 +
  130 +template <typename S, typename T> class is_streamable {
  131 + template <typename SS, typename TT>
  132 + static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
  133 +
  134 + template <typename, typename> static auto test(...) -> std::false_type;
  135 +
  136 + public:
  137 + static const bool value = decltype(test<S, T>(0))::value;
  138 +};
  139 +
  140 +/// Convert an object to a string (directly forward if this can become a string)
  141 +template <typename T, enable_if_t<std::is_constructible<std::string, T>::value, detail::enabler> = detail::dummy>
  142 +auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
  143 + return std::forward<T>(value);
  144 +}
  145 +
  146 +/// Convert an object to a string (streaming must be supported for that type)
  147 +template <typename T,
  148 + enable_if_t<!std::is_constructible<std::string, T>::value && is_streamable<std::stringstream, T>::value,
  149 + detail::enabler> = detail::dummy>
  150 +std::string to_string(T &&value) {
  151 + std::stringstream stream;
  152 + stream << value;
  153 + return stream.str();
  154 +}
  155 +
  156 +/// If conversion is not supported, return an empty string (streaming is not supported for that type)
  157 +template <typename T,
  158 + enable_if_t<!std::is_constructible<std::string, T>::value && !is_streamable<std::stringstream, T>::value,
  159 + detail::enabler> = detail::dummy>
  160 +std::string to_string(T &&) {
  161 + return std::string{};
  162 +}
  163 +
127 // Type name print 164 // Type name print
128 165
129 /// Was going to be based on 166 /// Was going to be based on
tests/AppTest.cpp
@@ -674,8 +674,8 @@ TEST_F(TApp, DefaultOpts) { @@ -674,8 +674,8 @@ TEST_F(TApp, DefaultOpts) {
674 int i = 3; 674 int i = 3;
675 std::string s = "HI"; 675 std::string s = "HI";
676 676
677 - app.add_option("-i,i", i, "", false);  
678 - app.add_option("-s,s", s, "", true); 677 + app.add_option("-i,i", i);
  678 + app.add_option("-s,s", s)->capture_default_str(); // Used to be different
679 679
680 args = {"-i2", "9"}; 680 args = {"-i2", "9"};
681 681
@@ -1511,7 +1511,7 @@ TEST_F(TApp, VectorDefaultedFixedString) { @@ -1511,7 +1511,7 @@ TEST_F(TApp, VectorDefaultedFixedString) {
1511 std::vector<std::string> strvec{"one"}; 1511 std::vector<std::string> strvec{"one"};
1512 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; 1512 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
1513 1513
1514 - CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3); 1514 + CLI::Option *opt = app.add_option("-s,--string", strvec, "")->expected(3)->capture_default_str();
1515 EXPECT_EQ(3, opt->get_expected()); 1515 EXPECT_EQ(3, opt->get_expected());
1516 1516
1517 args = {"--string", "mystring", "mystring2", "mystring3"}; 1517 args = {"--string", "mystring", "mystring2", "mystring3"};
@@ -1523,7 +1523,7 @@ TEST_F(TApp, VectorDefaultedFixedString) { @@ -1523,7 +1523,7 @@ TEST_F(TApp, VectorDefaultedFixedString) {
1523 TEST_F(TApp, DefaultedResult) { 1523 TEST_F(TApp, DefaultedResult) {
1524 std::string sval = "NA"; 1524 std::string sval = "NA";
1525 int ival; 1525 int ival;
1526 - auto opts = app.add_option("--string", sval, "", true); 1526 + auto opts = app.add_option("--string", sval)->capture_default_str();
1527 auto optv = app.add_option("--val", ival); 1527 auto optv = app.add_option("--val", ival);
1528 args = {}; 1528 args = {};
1529 run(); 1529 run();
@@ -1947,7 +1947,7 @@ TEST_F(TApp, FallthroughParents) { @@ -1947,7 +1947,7 @@ TEST_F(TApp, FallthroughParents) {
1947 1947
1948 TEST_F(TApp, OptionWithDefaults) { 1948 TEST_F(TApp, OptionWithDefaults) {
1949 int someint = 2; 1949 int someint = 2;
1950 - app.add_option("-a", someint, "", true); 1950 + app.add_option("-a", someint)->capture_default_str();
1951 1951
1952 args = {"-a1", "-a2"}; 1952 args = {"-a1", "-a2"};
1953 1953
@@ -2091,7 +2091,7 @@ TEST_F(TApp, CustomUserSepParse) { @@ -2091,7 +2091,7 @@ TEST_F(TApp, CustomUserSepParse) {
2091 2091
2092 app.remove_option(opt); 2092 app.remove_option(opt);
2093 2093
2094 - app.add_option("--idx", vals, "", true)->delimiter(','); 2094 + app.add_option("--idx", vals)->delimiter(',')->capture_default_str();
2095 run(); 2095 run();
2096 EXPECT_EQ(vals, std::vector<int>({1, 2, 3})); 2096 EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
2097 } 2097 }
@@ -2131,7 +2131,7 @@ TEST_F(TApp, CustomUserSepParse2) { @@ -2131,7 +2131,7 @@ TEST_F(TApp, CustomUserSepParse2) {
2131 2131
2132 app.remove_option(opt); 2132 app.remove_option(opt);
2133 2133
2134 - app.add_option("--idx", vals, "", true)->delimiter(','); 2134 + app.add_option("--idx", vals, "")->delimiter(',')->capture_default_str();
2135 run(); 2135 run();
2136 EXPECT_EQ(vals, std::vector<int>({1, 2})); 2136 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2137 } 2137 }
@@ -2185,13 +2185,13 @@ TEST_F(TApp, CustomUserSepParse4) { @@ -2185,13 +2185,13 @@ TEST_F(TApp, CustomUserSepParse4) {
2185 2185
2186 std::vector<int> vals; 2186 std::vector<int> vals;
2187 args = {"--idx", "1, 2"}; 2187 args = {"--idx", "1, 2"};
2188 - auto opt = app.add_option("--idx", vals)->delimiter(','); 2188 + auto opt = app.add_option("--idx", vals)->delimiter(',')->capture_default_str();
2189 run(); 2189 run();
2190 EXPECT_EQ(vals, std::vector<int>({1, 2})); 2190 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2191 2191
2192 app.remove_option(opt); 2192 app.remove_option(opt);
2193 2193
2194 - app.add_option("--idx", vals, "", true)->delimiter(','); 2194 + app.add_option("--idx", vals)->delimiter(',');
2195 run(); 2195 run();
2196 EXPECT_EQ(vals, std::vector<int>({1, 2})); 2196 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2197 } 2197 }
@@ -2207,7 +2207,7 @@ TEST_F(TApp, CustomUserSepParse5) { @@ -2207,7 +2207,7 @@ TEST_F(TApp, CustomUserSepParse5) {
2207 2207
2208 app.remove_option(opt); 2208 app.remove_option(opt);
2209 args = {"this", "is", "a", "test"}; 2209 args = {"this", "is", "a", "test"};
2210 - app.add_option("bar", bar, "bar", true); 2210 + app.add_option("bar", bar, "bar")->capture_default_str();
2211 run(); 2211 run();
2212 EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"})); 2212 EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
2213 } 2213 }
tests/CreationTest.cpp
@@ -728,4 +728,36 @@ TEST(ValidatorTests, ValidatorDefaults) { @@ -728,4 +728,36 @@ TEST(ValidatorTests, ValidatorDefaults) {
728 EXPECT_EQ(V2.get_description(), "check"); 728 EXPECT_EQ(V2.get_description(), "check");
729 EXPECT_TRUE(V2.get_active()); 729 EXPECT_TRUE(V2.get_active());
730 EXPECT_TRUE(V2.get_modifying()); 730 EXPECT_TRUE(V2.get_modifying());
  731 + // This class only support streaming in, not out
  732 +}
  733 +
  734 +class Unstreamable {
  735 + private:
  736 + int x_ = -1;
  737 +
  738 + public:
  739 + Unstreamable() {}
  740 + int get_x() const { return x_; }
  741 + void set_x(int x) { x_ = x; }
  742 +};
  743 +
  744 +std::istream &operator>>(std::istream &in, Unstreamable &value) {
  745 + int x;
  746 + in >> x;
  747 + value.set_x(x);
  748 + return in;
  749 +}
  750 +
  751 +TEST_F(TApp, MakeUnstreamableOptiions) {
  752 + Unstreamable value;
  753 + app.add_option("--value", value);
  754 +
  755 + // This used to fail to build, since it tries to stream from Unstreamable
  756 + app.add_option("--value2", value, "", false);
  757 +
  758 + std::vector<Unstreamable> values;
  759 + app.add_option("--values", values);
  760 +
  761 + // This used to fail to build, since it tries to stream from Unstreamable
  762 + app.add_option("--values2", values, "", false);
731 } 763 }
tests/DeprecatedTest.cpp
1 #include "app_helper.hpp" 1 #include "app_helper.hpp"
2 2
  3 +#include "gmock/gmock.h"
  4 +
  5 +using ::testing::HasSubstr;
  6 +using ::testing::Not;
  7 +
3 TEST(Deprecated, Emtpy) { 8 TEST(Deprecated, Emtpy) {
4 // No deprecated features at this time. 9 // No deprecated features at this time.
5 EXPECT_TRUE(true); 10 EXPECT_TRUE(true);
@@ -326,3 +331,212 @@ TEST_F(TApp, AddRemoveSetItemsNoCase) { @@ -326,3 +331,212 @@ TEST_F(TApp, AddRemoveSetItemsNoCase) {
326 args = {"--type2", "TYpE2"}; 331 args = {"--type2", "TYpE2"};
327 EXPECT_THROW(run(), CLI::ValidationError); 332 EXPECT_THROW(run(), CLI::ValidationError);
328 } 333 }
  334 +
  335 +TEST(THelp, Defaults) {
  336 + CLI::App app{"My prog"};
  337 +
  338 + int one{1}, two{2};
  339 + app.add_option("--one", one, "Help for one", true);
  340 + app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4}));
  341 +
  342 + std::string help = app.help();
  343 +
  344 + EXPECT_THAT(help, HasSubstr("--one"));
  345 + EXPECT_THAT(help, HasSubstr("--set"));
  346 + EXPECT_THAT(help, HasSubstr("1"));
  347 + EXPECT_THAT(help, HasSubstr("=2"));
  348 + EXPECT_THAT(help, HasSubstr("2,3,4"));
  349 +}
  350 +
  351 +TEST(THelp, VectorOpts) {
  352 + CLI::App app{"My prog"};
  353 + std::vector<int> x = {1, 2};
  354 + app.add_option("-q,--quick", x, "", true);
  355 +
  356 + std::string help = app.help();
  357 +
  358 + EXPECT_THAT(help, HasSubstr("INT=[1,2] ..."));
  359 +}
  360 +
  361 +TEST(THelp, SetLower) {
  362 + CLI::App app{"My prog"};
  363 +
  364 + std::string def{"One"};
  365 + app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"}));
  366 +
  367 + std::string help = app.help();
  368 +
  369 + EXPECT_THAT(help, HasSubstr("--set"));
  370 + EXPECT_THAT(help, HasSubstr("=One"));
  371 + EXPECT_THAT(help, HasSubstr("oNe"));
  372 + EXPECT_THAT(help, HasSubstr("twO"));
  373 + EXPECT_THAT(help, HasSubstr("THREE"));
  374 +}
  375 +
  376 +TEST(THelp, ChangingSetDefaulted) {
  377 + CLI::App app;
  378 +
  379 + std::set<int> vals{1, 2, 3};
  380 + int val = 2;
  381 + app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals));
  382 +
  383 + std::string help = app.help();
  384 +
  385 + EXPECT_THAT(help, HasSubstr("1"));
  386 + EXPECT_THAT(help, Not(HasSubstr("4")));
  387 +
  388 + vals.insert(4);
  389 + vals.erase(1);
  390 +
  391 + help = app.help();
  392 +
  393 + EXPECT_THAT(help, Not(HasSubstr("1")));
  394 + EXPECT_THAT(help, HasSubstr("4"));
  395 +}
  396 +
  397 +TEST(THelp, ChangingCaselessSetDefaulted) {
  398 + CLI::App app;
  399 +
  400 + std::set<std::string> vals{"1", "2", "3"};
  401 + std::string val = "2";
  402 + app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case));
  403 +
  404 + std::string help = app.help();
  405 +
  406 + EXPECT_THAT(help, HasSubstr("1"));
  407 + EXPECT_THAT(help, Not(HasSubstr("4")));
  408 +
  409 + vals.insert("4");
  410 + vals.erase("1");
  411 +
  412 + help = app.help();
  413 +
  414 + EXPECT_THAT(help, Not(HasSubstr("1")));
  415 + EXPECT_THAT(help, HasSubstr("4"));
  416 +}
  417 +
  418 +TEST_F(TApp, DefaultOpts) {
  419 +
  420 + int i = 3;
  421 + std::string s = "HI";
  422 +
  423 + app.add_option("-i,i", i, "", false);
  424 + app.add_option("-s,s", s, "", true);
  425 +
  426 + args = {"-i2", "9"};
  427 +
  428 + run();
  429 +
  430 + EXPECT_EQ(1u, app.count("i"));
  431 + EXPECT_EQ(1u, app.count("-s"));
  432 + EXPECT_EQ(2, i);
  433 + EXPECT_EQ("9", s);
  434 +}
  435 +
  436 +TEST_F(TApp, VectorDefaultedFixedString) {
  437 + std::vector<std::string> strvec{"one"};
  438 + std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
  439 +
  440 + CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3);
  441 + EXPECT_EQ(3, opt->get_expected());
  442 +
  443 + args = {"--string", "mystring", "mystring2", "mystring3"};
  444 + run();
  445 + EXPECT_EQ(3u, app.count("--string"));
  446 + EXPECT_EQ(answer, strvec);
  447 +}
  448 +
  449 +TEST_F(TApp, DefaultedResult) {
  450 + std::string sval = "NA";
  451 + int ival;
  452 + auto opts = app.add_option("--string", sval, "", true);
  453 + auto optv = app.add_option("--val", ival);
  454 + args = {};
  455 + run();
  456 + EXPECT_EQ(sval, "NA");
  457 + std::string nString;
  458 + opts->results(nString);
  459 + EXPECT_EQ(nString, "NA");
  460 + int newIval;
  461 + EXPECT_THROW(optv->results(newIval), CLI::ConversionError);
  462 + optv->default_str("442");
  463 + optv->results(newIval);
  464 + EXPECT_EQ(newIval, 442);
  465 +}
  466 +
  467 +TEST_F(TApp, OptionWithDefaults) {
  468 + int someint = 2;
  469 + app.add_option("-a", someint, "", true);
  470 +
  471 + args = {"-a1", "-a2"};
  472 +
  473 + EXPECT_THROW(run(), CLI::ArgumentMismatch);
  474 +}
  475 +
  476 +// #209
  477 +TEST_F(TApp, CustomUserSepParse) {
  478 +
  479 + std::vector<int> vals = {1, 2, 3};
  480 + args = {"--idx", "1,2,3"};
  481 + auto opt = app.add_option("--idx", vals)->delimiter(',');
  482 + run();
  483 + EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
  484 + std::vector<int> vals2;
  485 + // check that the results vector gets the results in the same way
  486 + opt->results(vals2);
  487 + EXPECT_EQ(vals2, vals);
  488 +
  489 + app.remove_option(opt);
  490 +
  491 + app.add_option("--idx", vals, "", true)->delimiter(',');
  492 + run();
  493 + EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
  494 +}
  495 +
  496 +// #209
  497 +TEST_F(TApp, CustomUserSepParse2) {
  498 +
  499 + std::vector<int> vals = {1, 2, 3};
  500 + args = {"--idx", "1,2,"};
  501 + auto opt = app.add_option("--idx", vals)->delimiter(',');
  502 + run();
  503 + EXPECT_EQ(vals, std::vector<int>({1, 2}));
  504 +
  505 + app.remove_option(opt);
  506 +
  507 + app.add_option("--idx", vals, "", true)->delimiter(',');
  508 + run();
  509 + EXPECT_EQ(vals, std::vector<int>({1, 2}));
  510 +}
  511 +//
  512 +// #209
  513 +TEST_F(TApp, CustomUserSepParse4) {
  514 +
  515 + std::vector<int> vals;
  516 + args = {"--idx", "1, 2"};
  517 + auto opt = app.add_option("--idx", vals, "", true)->delimiter(',');
  518 + run();
  519 + EXPECT_EQ(vals, std::vector<int>({1, 2}));
  520 +
  521 + app.remove_option(opt);
  522 +
  523 + app.add_option("--idx", vals)->delimiter(',');
  524 + run();
  525 + EXPECT_EQ(vals, std::vector<int>({1, 2}));
  526 +}
  527 +
  528 +// #218
  529 +TEST_F(TApp, CustomUserSepParse5) {
  530 +
  531 + std::vector<std::string> bar;
  532 + args = {"this", "is", "a", "test"};
  533 + auto opt = app.add_option("bar", bar, "bar");
  534 + run();
  535 + EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
  536 +
  537 + app.remove_option(opt);
  538 + args = {"this", "is", "a", "test"};
  539 + app.add_option("bar", bar, "bar", true);
  540 + run();
  541 + EXPECT_EQ(bar, std::vector<std::string>({"this", "is", "a", "test"}));
  542 +}
tests/HelpTest.cpp
@@ -119,7 +119,7 @@ TEST(THelp, MultiOpts) { @@ -119,7 +119,7 @@ TEST(THelp, MultiOpts) {
119 TEST(THelp, VectorOpts) { 119 TEST(THelp, VectorOpts) {
120 CLI::App app{"My prog"}; 120 CLI::App app{"My prog"};
121 std::vector<int> x = {1, 2}; 121 std::vector<int> x = {1, 2};
122 - app.add_option("-q,--quick", x, "", true); 122 + app.add_option("-q,--quick", x)->capture_default_str();
123 123
124 std::string help = app.help(); 124 std::string help = app.help();
125 125
@@ -299,8 +299,8 @@ TEST(THelp, IntDefaults) { @@ -299,8 +299,8 @@ TEST(THelp, IntDefaults) {
299 CLI::App app{"My prog"}; 299 CLI::App app{"My prog"};
300 300
301 int one{1}, two{2}; 301 int one{1}, two{2};
302 - app.add_option("--one", one, "Help for one", true);  
303 - app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4})); 302 + app.add_option("--one", one, "Help for one")->capture_default_str();
  303 + app.add_option("--set", two, "Help for set")->capture_default_str()->check(CLI::IsMember({2, 3, 4}));
304 304
305 std::string help = app.help(); 305 std::string help = app.help();
306 306
@@ -313,9 +313,10 @@ TEST(THelp, IntDefaults) { @@ -313,9 +313,10 @@ TEST(THelp, IntDefaults) {
313 313
314 TEST(THelp, SetLower) { 314 TEST(THelp, SetLower) {
315 CLI::App app{"My prog"}; 315 CLI::App app{"My prog"};
  316 + app.option_defaults()->always_capture_default();
316 317
317 std::string def{"One"}; 318 std::string def{"One"};
318 - app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"})); 319 + app.add_option("--set", def, "Help for set")->check(CLI::IsMember({"oNe", "twO", "THREE"}));
319 320
320 std::string help = app.help(); 321 std::string help = app.help();
321 322
@@ -866,7 +867,7 @@ TEST(THelp, ChangingSetDefaulted) { @@ -866,7 +867,7 @@ TEST(THelp, ChangingSetDefaulted) {
866 867
867 std::set<int> vals{1, 2, 3}; 868 std::set<int> vals{1, 2, 3};
868 int val = 2; 869 int val = 2;
869 - app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals)); 870 + app.add_option("--val", val, "")->check(CLI::IsMember(&vals))->capture_default_str();
870 871
871 std::string help = app.help(); 872 std::string help = app.help();
872 873
@@ -881,6 +882,7 @@ TEST(THelp, ChangingSetDefaulted) { @@ -881,6 +882,7 @@ TEST(THelp, ChangingSetDefaulted) {
881 EXPECT_THAT(help, Not(HasSubstr("1"))); 882 EXPECT_THAT(help, Not(HasSubstr("1")));
882 EXPECT_THAT(help, HasSubstr("4")); 883 EXPECT_THAT(help, HasSubstr("4"));
883 } 884 }
  885 +
884 TEST(THelp, ChangingCaselessSet) { 886 TEST(THelp, ChangingCaselessSet) {
885 CLI::App app; 887 CLI::App app;
886 888
@@ -904,10 +906,11 @@ TEST(THelp, ChangingCaselessSet) { @@ -904,10 +906,11 @@ TEST(THelp, ChangingCaselessSet) {
904 906
905 TEST(THelp, ChangingCaselessSetDefaulted) { 907 TEST(THelp, ChangingCaselessSetDefaulted) {
906 CLI::App app; 908 CLI::App app;
  909 + app.option_defaults()->always_capture_default();
907 910
908 std::set<std::string> vals{"1", "2", "3"}; 911 std::set<std::string> vals{"1", "2", "3"};
909 std::string val = "2"; 912 std::string val = "2";
910 - app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case)); 913 + app.add_option("--val", val)->check(CLI::IsMember(&vals, CLI::ignore_case));
911 914
912 std::string help = app.help(); 915 std::string help = app.help();
913 916
@@ -922,3 +925,50 @@ TEST(THelp, ChangingCaselessSetDefaulted) { @@ -922,3 +925,50 @@ TEST(THelp, ChangingCaselessSetDefaulted) {
922 EXPECT_THAT(help, Not(HasSubstr("1"))); 925 EXPECT_THAT(help, Not(HasSubstr("1")));
923 EXPECT_THAT(help, HasSubstr("4")); 926 EXPECT_THAT(help, HasSubstr("4"));
924 } 927 }
  928 +
  929 +// New defaults tests (1.8)
  930 +
  931 +TEST(THelp, ChangingDefaults) {
  932 +
  933 + CLI::App app;
  934 +
  935 + std::vector<int> x = {1, 2};
  936 + CLI::Option *opt = app.add_option("-q,--quick", x);
  937 + x = {3, 4};
  938 +
  939 + opt->capture_default_str();
  940 +
  941 + x = {5, 6};
  942 + std::string help = app.help();
  943 +
  944 + EXPECT_THAT(help, HasSubstr("INT=[3,4] ..."));
  945 +}
  946 +
  947 +TEST(THelp, ChangingDefaultsWithAutoCapture) {
  948 +
  949 + CLI::App app;
  950 + app.option_defaults()->always_capture_default();
  951 +
  952 + std::vector<int> x = {1, 2};
  953 + app.add_option("-q,--quick", x);
  954 + x = {3, 4};
  955 +
  956 + std::string help = app.help();
  957 +
  958 + EXPECT_THAT(help, HasSubstr("INT=[1,2] ..."));
  959 +}
  960 +
  961 +TEST(THelp, FunctionDefaultString) {
  962 +
  963 + CLI::App app;
  964 +
  965 + std::vector<int> x = {1, 2};
  966 + CLI::Option *opt = app.add_option("-q,--quick", x);
  967 +
  968 + opt->default_function([]() { return std::string("Powerful"); });
  969 + opt->capture_default_str();
  970 +
  971 + std::string help = app.help();
  972 +
  973 + EXPECT_THAT(help, HasSubstr("INT=Powerful"));
  974 +}
tests/HelpersTest.cpp
@@ -7,6 +7,24 @@ @@ -7,6 +7,24 @@
7 #include <fstream> 7 #include <fstream>
8 #include <string> 8 #include <string>
9 9
  10 +class NotStreamable {};
  11 +
  12 +class Streamable {};
  13 +
  14 +std::ostream &operator<<(std::ostream &out, const Streamable &) { return out << "Streamable"; }
  15 +
  16 +TEST(TypeTools, Streaming) {
  17 +
  18 + EXPECT_EQ(CLI::detail::to_string(NotStreamable{}), "");
  19 +
  20 + EXPECT_EQ(CLI::detail::to_string(Streamable{}), "Streamable");
  21 +
  22 + EXPECT_EQ(CLI::detail::to_string(5), "5");
  23 +
  24 + EXPECT_EQ(CLI::detail::to_string("string"), std::string("string"));
  25 + EXPECT_EQ(CLI::detail::to_string(std::string("string")), std::string("string"));
  26 +}
  27 +
10 TEST(Split, SimpleByToken) { 28 TEST(Split, SimpleByToken) {
11 auto out = CLI::detail::split("one.two.three", '.'); 29 auto out = CLI::detail::split("one.two.three", '.');
12 ASSERT_EQ(3u, out.size()); 30 ASSERT_EQ(3u, out.size());