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 118 script:
119 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 121 install: skip
132 122  
133 123 script:
... ...
CHANGELOG.md
1 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 7 * New `CLI::IsMember` validator replaces set validation [#222]
6 8 * IsMember also supports container of pairs, transform allows modification of result [#228]
7 9 * Much more powerful flags with different values [#211], general types [#235]
8 10 * `add_option` now supports bool due to unified bool handling [#211]
9 11 * Support for composable unnamed subcommands [#216]
  12 +* Reparsing is better supported with `.remaining_for_passthrough()` [#265]
10 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 15 * Minimum required Boost for optional Optionals has been corrected to 1.61 [#226]
13 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 19 * Validators can be negated with `!` [#230], and now handle tname functions [#228]
15 20 * Better enum support and streaming helper [#233] and [#228]
16 21 * Cleanup for shadow warnings [#232]
  22 +* Better alignment on multiline descriptions [#269]
  23 +* Better support for aarch64 [#266]
17 24  
18 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 28 > * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))`
21 29 > * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember`
22 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 47 [#233]: https://github.com/CLIUtils/CLI11/pull/233
40 48 [#235]: https://github.com/CLIUtils/CLI11/pull/235
41 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 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 33 - [Option types](#option-types)
34 34 - [Example](#example)
35 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 41 - [Getting Results](#getting-results) ๐Ÿšง
42 42 - [Subcommands](#subcommands)
43 43 - [Subcommand options](#subcommand-options)
... ... @@ -64,7 +64,7 @@ CLI11 provides all the features you expect in a powerful command line parser, wi
64 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 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 69 ### Why write another CLI parser?
70 70  
... ... @@ -74,7 +74,7 @@ An acceptable CLI parser library should be all of the following:
74 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 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 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 78 - Clear help printing.
79 79 - Nice error messages.
80 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 193  
194 194 app.add_option(option_name,
195 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 198 app.add_option_function<type>(option_name,
200 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 224 -app.add_set(option_name,
226 225 - variable_to_bind_to, // Same type as stored by set
227 226 - set_of_possible_options, // Set will be copied, ignores changes
228   -- help_string="",
229   -- default=false)
  227 +- help_string="")
230 228 -app.add_mutable_set(... // ๐Ÿ†• Set can change later, keeps reference
231 229 -app.add_set_ignore_case(... // String only
232 230 -app.add_mutable_set_ignore_case(... // ๐Ÿ†• String only
... ... @@ -236,9 +234,9 @@ Option_group *app.add_option_group(name,description); // ๐Ÿšง
236 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 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 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 284 - `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
287 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 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 288 - `->description(str)`: ๐Ÿ†• Set/change the description.
291 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 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 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 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 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 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 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 325  
325 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 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 330 You can access a vector of pointers to the parsed options in the original order using `parse_order()`.
330 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 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 352 These Validators can be used by simply passing the name into the `check` or `transform` methods on an option
  353 +
352 354 ```cpp
353 355 ->check(CLI::ExistingFile);
354 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 361 ```cpp
359 362 ->check(CLI::Range(0,10)|CLI::Range(20,30));
360 363 ```
  364 +
361 365 will produce a check to ensure a value is between 0 and 10 or 20 and 30.
  366 +
362 367 ```cpp
363 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 373 ##### Transforming Validators
368 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 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 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 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 387 After specifying a map of options, you can also specify "filter" just like in `CLI::IsMember`.
382 388 Here are some examples (`Transformer` and `CheckedTransformer` are interchangeable in the examples)
383 389 of `Transformer`:
... ... @@ -385,12 +391,12 @@ of `Transformer`:
385 391 * `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
386 392  
387 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 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 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 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 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 402 The operation function of a Validator can be set via
... ... @@ -406,7 +412,7 @@ The check can later be activated through
406 412 opt->get_validator("range")->active();
407 413 ```
408 414  
409   -##### Custom Validators๐Ÿšง
  415 +##### Custom Validators ๐Ÿšง
410 416  
411 417 A validator object with a custom function can be created via
412 418 ```cpp
... ... @@ -427,10 +433,10 @@ opt-&gt;get_validator(name);
427 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 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 441 #### Getting results
436 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 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 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 467 #### Subcommand options
462 468  
... ... @@ -467,22 +473,22 @@ There are several options that are supported on the main app and subcommands and
467 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 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 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 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 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 482 - `.require_option(min, max)`: ๐Ÿšง Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
477 483 - `.require_subcommand()`: Require 1 or more subcommands.
478 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 485 - `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
480 486 - `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand.
481 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 489 - `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
484 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 492 - `.get_parent()`: Get the parent App or `nullptr` if called on master App.
487 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 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 520  
515 521 #### Callbacks
516 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 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 554 3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand.
549 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 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 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 590 ```cpp
585 591 CLI::TriggerOn(group1_pointer, triggered_group);
... ...
examples/json.cpp
... ... @@ -28,8 +28,8 @@ class ConfigJSON : public CLI::Config {
28 28 j[name] = opt->results();
29 29  
30 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 34 // Flag, one passed
35 35 } else if(opt->count() == 1) {
... ...
include/CLI/App.hpp
... ... @@ -437,17 +437,33 @@ class App {
437 437 Option *add_option(std::string option_name,
438 438 callback_t option_callback,
439 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 444 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
444 445 return *v == myopt;
445 446 }) == std::end(options_)) {
446 447 options_.emplace_back();
447 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 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 465 return option.get();
  466 +
451 467 } else
452 468 throw OptionAlreadyAdded(myopt.get_name());
453 469 }
... ... @@ -456,12 +472,16 @@ class App {
456 472 template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy>
457 473 Option *add_option(std::string option_name,
458 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 483 opt->type_name(detail::type_name<T>());
  484 +
465 485 return opt;
466 486 }
467 487  
... ... @@ -471,7 +491,7 @@ class App {
471 491 const std::function<void(const T &)> &func, ///< the callback to execute
472 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 495 T variable;
476 496 bool result = detail::lexical_cast(res[0], variable);
477 497 if(result) {
... ... @@ -484,6 +504,7 @@ class App {
484 504 opt->type_name(detail::type_name<T>());
485 505 return opt;
486 506 }
  507 +
487 508 /// Add option with no description or variable assignment
488 509 Option *add_option(std::string option_name) {
489 510 return add_option(option_name, CLI::callback_t(), std::string{}, false);
... ... @@ -497,34 +518,14 @@ class App {
497 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 521 /// Add option for vectors
522 522 template <typename T>
523 523 Option *add_option(std::string option_name,
524 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 529 bool retval = true;
529 530 variable.clear();
530 531 variable.reserve(res.size());
... ... @@ -536,35 +537,18 @@ class App {
536 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 550 opt->type_name(detail::type_name<T>())->type_size(-1);
565 551  
566   - if(defaulted)
567   - opt->default_str("[" + detail::join(variable) + "]");
568 552 return opt;
569 553 }
570 554  
... ... @@ -995,13 +979,16 @@ class App {
995 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 983 std::stringstream out;
1002 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 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 32 value = detail::ini_join(opt->results());
33 33  
34 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 37 // Flag, one passed
38 38 } else if(opt->count() == 1) {
39 39 value = "true";
... ...
include/CLI/Formatter.hpp
... ... @@ -232,8 +232,8 @@ inline std::string Formatter::make_option_opts(const Option *opt) const {
232 232 if(opt->get_type_size() != 0) {
233 233 if(!opt->get_type_name().empty())
234 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 237 if(opt->get_expected() > 1)
238 238 out << " x " << opt->get_expected();
239 239 if(opt->get_expected() == -1)
... ...
include/CLI/Option.hpp
... ... @@ -50,10 +50,16 @@ template &lt;typename CRTP&gt; class OptionBase {
50 50  
51 51 /// Allow this option to be given in a configuration file
52 52 bool configurable_{true};
  53 +
53 54 /// Disable overriding flag values with '=value'
54 55 bool disable_flag_override_{false};
  56 +
55 57 /// Specify a delimiter character for vector arguments
56 58 char delimiter_{'\0'};
  59 +
  60 + /// Automatically capture default value
  61 + bool always_capture_default_{false};
  62 +
57 63 /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too)
58 64 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
59 65  
... ... @@ -66,6 +72,7 @@ template &lt;typename CRTP&gt; class OptionBase {
66 72 other->configurable(configurable_);
67 73 other->disable_flag_override(disable_flag_override_);
68 74 other->delimiter(delimiter_);
  75 + other->always_capture_default(always_capture_default_);
69 76 other->multi_option_policy(multi_option_policy_);
70 77 }
71 78  
... ... @@ -87,6 +94,11 @@ template &lt;typename CRTP&gt; class OptionBase {
87 94 /// Support Plumbum term
88 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 102 // Getters
91 103  
92 104 /// Get the group of this option
... ... @@ -107,7 +119,12 @@ template &lt;typename CRTP&gt; class OptionBase {
107 119 /// The status of configurable
108 120 bool get_disable_flag_override() const { return disable_flag_override_; }
109 121  
  122 + /// Get the current delimeter char
110 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 128 /// The status of the multi option policy
112 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 236 /// The description for help strings
220 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 242 /// A human readable type value, set when App creates this
226 243 ///
227 244 /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents.
228 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 251 /// @name Configuration
... ... @@ -277,10 +294,8 @@ class Option : public OptionBase&lt;Option&gt; {
277 294 Option(std::string option_name,
278 295 std::string option_description,
279 296 std::function<bool(results_t)> callback,
280   - bool defaulted,
281 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 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 321  
307 322 /// Set the number of expected arguments (Flags don't use this)
308 323 Option *expected(int value) {
  324 +
309 325 // Break if this is a flag
310 326 if(type_size_ == 0)
311 327 throw IncorrectConstruction::SetFlag(get_name(true, true));
... ... @@ -532,8 +548,12 @@ class Option : public OptionBase&lt;Option&gt; {
532 548 /// The set of options excluded
533 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 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 558 /// Get the callback function
539 559 callback_t get_callback() const { return callback_; }
... ... @@ -571,9 +591,6 @@ class Option : public OptionBase&lt;Option&gt; {
571 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 594 /// True if the argument can be given directly
578 595 bool get_positional() const { return pname_.length() > 0; }
579 596  
... ... @@ -843,7 +860,7 @@ class Option : public OptionBase&lt;Option&gt; {
843 860 void results(T &output) const {
844 861 bool retval;
845 862 if(results_.empty()) {
846   - retval = detail::lexical_cast(defaultval_, output);
  863 + retval = detail::lexical_cast(default_str_, output);
847 864 } else if(results_.size() == 1) {
848 865 retval = detail::lexical_cast(results_[0], output);
849 866 } else {
... ... @@ -917,13 +934,27 @@ class Option : public OptionBase&lt;Option&gt; {
917 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 952 Option *default_str(std::string val) {
922   - defaultval_ = val;
  953 + default_str_ = val;
923 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 958 Option *default_val(std::string val) {
928 959 default_str(val);
929 960 auto old_results = results_;
... ...
include/CLI/Optional.hpp
... ... @@ -46,6 +46,7 @@
46 46 #endif
47 47 #if CLI11_BOOST_OPTIONAL
48 48 #include <boost/optional.hpp>
  49 +#include <boost/optional/optional_io.hpp>
49 50 #endif
50 51 // [CLI11:verbatim]
51 52  
... ...
include/CLI/TypeTools.hpp
... ... @@ -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 164 // Type name print
128 165  
129 166 /// Was going to be based on
... ...
tests/AppTest.cpp
... ... @@ -674,8 +674,8 @@ TEST_F(TApp, DefaultOpts) {
674 674 int i = 3;
675 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 680 args = {"-i2", "9"};
681 681  
... ... @@ -1511,7 +1511,7 @@ TEST_F(TApp, VectorDefaultedFixedString) {
1511 1511 std::vector<std::string> strvec{"one"};
1512 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 1515 EXPECT_EQ(3, opt->get_expected());
1516 1516  
1517 1517 args = {"--string", "mystring", "mystring2", "mystring3"};
... ... @@ -1523,7 +1523,7 @@ TEST_F(TApp, VectorDefaultedFixedString) {
1523 1523 TEST_F(TApp, DefaultedResult) {
1524 1524 std::string sval = "NA";
1525 1525 int ival;
1526   - auto opts = app.add_option("--string", sval, "", true);
  1526 + auto opts = app.add_option("--string", sval)->capture_default_str();
1527 1527 auto optv = app.add_option("--val", ival);
1528 1528 args = {};
1529 1529 run();
... ... @@ -1947,7 +1947,7 @@ TEST_F(TApp, FallthroughParents) {
1947 1947  
1948 1948 TEST_F(TApp, OptionWithDefaults) {
1949 1949 int someint = 2;
1950   - app.add_option("-a", someint, "", true);
  1950 + app.add_option("-a", someint)->capture_default_str();
1951 1951  
1952 1952 args = {"-a1", "-a2"};
1953 1953  
... ... @@ -2091,7 +2091,7 @@ TEST_F(TApp, CustomUserSepParse) {
2091 2091  
2092 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 2095 run();
2096 2096 EXPECT_EQ(vals, std::vector<int>({1, 2, 3}));
2097 2097 }
... ... @@ -2131,7 +2131,7 @@ TEST_F(TApp, CustomUserSepParse2) {
2131 2131  
2132 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 2135 run();
2136 2136 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2137 2137 }
... ... @@ -2185,13 +2185,13 @@ TEST_F(TApp, CustomUserSepParse4) {
2185 2185  
2186 2186 std::vector<int> vals;
2187 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 2189 run();
2190 2190 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2191 2191  
2192 2192 app.remove_option(opt);
2193 2193  
2194   - app.add_option("--idx", vals, "", true)->delimiter(',');
  2194 + app.add_option("--idx", vals)->delimiter(',');
2195 2195 run();
2196 2196 EXPECT_EQ(vals, std::vector<int>({1, 2}));
2197 2197 }
... ... @@ -2207,7 +2207,7 @@ TEST_F(TApp, CustomUserSepParse5) {
2207 2207  
2208 2208 app.remove_option(opt);
2209 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 2211 run();
2212 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 728 EXPECT_EQ(V2.get_description(), "check");
729 729 EXPECT_TRUE(V2.get_active());
730 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 1 #include "app_helper.hpp"
2 2  
  3 +#include "gmock/gmock.h"
  4 +
  5 +using ::testing::HasSubstr;
  6 +using ::testing::Not;
  7 +
3 8 TEST(Deprecated, Emtpy) {
4 9 // No deprecated features at this time.
5 10 EXPECT_TRUE(true);
... ... @@ -326,3 +331,212 @@ TEST_F(TApp, AddRemoveSetItemsNoCase) {
326 331 args = {"--type2", "TYpE2"};
327 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 119 TEST(THelp, VectorOpts) {
120 120 CLI::App app{"My prog"};
121 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 124 std::string help = app.help();
125 125  
... ... @@ -299,8 +299,8 @@ TEST(THelp, IntDefaults) {
299 299 CLI::App app{"My prog"};
300 300  
301 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 305 std::string help = app.help();
306 306  
... ... @@ -313,9 +313,10 @@ TEST(THelp, IntDefaults) {
313 313  
314 314 TEST(THelp, SetLower) {
315 315 CLI::App app{"My prog"};
  316 + app.option_defaults()->always_capture_default();
316 317  
317 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 321 std::string help = app.help();
321 322  
... ... @@ -866,7 +867,7 @@ TEST(THelp, ChangingSetDefaulted) {
866 867  
867 868 std::set<int> vals{1, 2, 3};
868 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 872 std::string help = app.help();
872 873  
... ... @@ -881,6 +882,7 @@ TEST(THelp, ChangingSetDefaulted) {
881 882 EXPECT_THAT(help, Not(HasSubstr("1")));
882 883 EXPECT_THAT(help, HasSubstr("4"));
883 884 }
  885 +
884 886 TEST(THelp, ChangingCaselessSet) {
885 887 CLI::App app;
886 888  
... ... @@ -904,10 +906,11 @@ TEST(THelp, ChangingCaselessSet) {
904 906  
905 907 TEST(THelp, ChangingCaselessSetDefaulted) {
906 908 CLI::App app;
  909 + app.option_defaults()->always_capture_default();
907 910  
908 911 std::set<std::string> vals{"1", "2", "3"};
909 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 915 std::string help = app.help();
913 916  
... ... @@ -922,3 +925,50 @@ TEST(THelp, ChangingCaselessSetDefaulted) {
922 925 EXPECT_THAT(help, Not(HasSubstr("1")));
923 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 7 #include <fstream>
8 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 28 TEST(Split, SimpleByToken) {
11 29 auto out = CLI::detail::split("one.two.three", '.');
12 30 ASSERT_EQ(3u, out.size());
... ...