diff --git a/.travis.yml b/.travis.yml index e58a01d6..b3956ed8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: - .ci/make_and_test.sh 17 # Check style/tidy - - compiler: clang + - compiler: clang env: - CHECK_STYLE=yes script: @@ -104,6 +104,20 @@ matrix: conan upload "*" -c -r origin --all fi + # GCC 4.8 + - compiler: gcc + env: + - GCC_VER=4.8 + addons: + apt: + packages: + - g++-4.8 + install: + - export CC=gcc-4.8 + - export CXX=g++-4.8 + script: + - .ci/make_and_test.sh 11 + # macOS and clang - os: osx compiler: clang diff --git a/CHANGELOG.md b/CHANGELOG.md index 705a24af..e0aba362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,29 @@ -## Version 1.8: Flags and Sets +## Version 1.8: Sets and Flags -This version adds inverted flags, which can cancel or reduce the count of flags, and can also support basic number assignment. 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. +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. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support basic number assignment. 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. +* New `CL::IsMember` validator replaces set validation [#222] * Much more powerful flags with different values [#211] +* `add_option` now supports bool due to unified bool handling [#211] * Support for composable unnamed subcommands [#216] * Custom vector separator [#209], [#221] * Validators added for IP4 addresses and positive numbers [#210] +> ### Converting from CLI11 1.7: +> +> * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))` +> * The `_mutable` versions of this can be replaced by passing a pointer or shared pointer into `IsMember` +> * The `_ignore_case` version of this can be replaced by adding `CLI::ignore_case` to the argument list in `IsMember` +> * The `_ignore_underscore` version of this can be replaced by adding `CLI::ignore_underscore` to the argument list in `IsMember` +> * The `_ignore_case_underscore` version of this can be replaced by adding both functions listed above to the argument list in `IsMember` +> * An error with sets now produces a `ValidationError` instead of a `ConversionError` + [#209]: https://github.com/CLIUtils/CLI11/pull/209 [#210]: https://github.com/CLIUtils/CLI11/pull/210 [#211]: https://github.com/CLIUtils/CLI11/pull/211 [#216]: https://github.com/CLIUtils/CLI11/pull/216 [#221]: https://github.com/CLIUtils/CLI11/pull/221 +[#222]: https://github.com/CLIUtils/CLI11/pull/222 ## Version 1.7.1: Quick patch diff --git a/README.md b/README.md index 89478c10..db61f643 100644 --- a/README.md +++ b/README.md @@ -144,12 +144,15 @@ GTEST_COLOR=1 CTEST_OUTPUT_ON_FAILURE=1 make test To set up, add options, and run, your main function will look something like this: ```cpp -CLI::App app{"App description"}; +int main(int charc, char** argv) { + CLI::App app{"App description"}; -std::string filename = "default"; -app.add_option("-f,--file", filename, "A help string"); + std::string filename = "default"; + app.add_option("-f,--file", filename, "A help string"); -CLI11_PARSE(app, argc, argv); + CLI11_PARSE(app, argc, argv); + return 0; +} ```
Note: If you don't like macros, this is what that macro expands to: (click to expand)

@@ -175,7 +178,7 @@ While all options internally are the same type, there are several ways to add an ```cpp app.add_option(option_name, - variable_to_bind_to, // int, float, vector, or string-like + variable_to_bind_to, // bool, int, float, vector, or string-like help_string="", default=false) @@ -196,24 +199,10 @@ app.add_flag_function(option_name, function , help_string="") -app.add_set(option_name, - variable_to_bind_to, // Same type as stored by set - set_of_possible_options, // Set will be copied, ignores changes - help_string="", - default=false) -app.add_mutable_set(... // Set can change later, keeps reference - -app.add_set_ignore_case(... // String only -app.add_mutable_set_ignore_case(... // String only -app.add_set_ignore_underscore(... // String only -app.add_mutable_set_ignore_underscore(... // String only -app.add_set_ignore_case_underscore(... // String only -app.add_mutable_set_ignore_case_underscore(... // String only - App* subcom = app.add_subcommand(name, description); ``` -An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. 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 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` or `add_set`. The set options allow your users to pick from a set of predefined options, and you can use an initializer list directly if you like. If you need to modify the set later, use the `mutable` forms. +An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. 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 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`. The `add_option_function(...` 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. @@ -229,7 +218,7 @@ app.add_flag_function(option_name, help_string="") ``` -allow a syntax for the option names to default particular options to a false value if some flags are passed. For example: +which allow a syntax for the option names to default particular options to a false value if some flags are passed. For example: ```cpp app.add_flag("--flag,!--no-flag,result,"help for flag");` @@ -271,6 +260,7 @@ Before parsing, you can set the following options: - `->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 - `->description(str)`: Set/change the description. - `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy). +- `->check(CLI::IsMember(...))`: Require an option be a member of a given set. See below for options. - `->check(CLI::ExistingFile)`: Requires that the file exists if given. - `->check(CLI::ExistingDirectory)`: Requires that the directory exists. - `->check(CLI::ExistingPath)`: Requires that the path (file or directory) exists. @@ -282,8 +272,19 @@ Before parsing, you can set the following options: - `->each(void(std::string)>`: Run this function on each value received, as it is received. - `->configurable(false)`: Disable this option from being in a configuration file. + These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check 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 `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector` of results. Validate can also be a subclass of `CLI::Validator`, in which case it can also set the type name and can be combined with `&` and `|` (all built-in validators are this sort). +The `IsMember` validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this validator; the container just needs to be iterable and have a `::value_type`. The type should be convertible from a string. You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set. +After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`. +Here are some examples +of `IsMember`: + +- `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices. +- `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too. +- `CLI::IsMember(std::set({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`. +- `auto p = std::make_shared>(std::initializer_list("one", "two")); CLI::IsMember(p)`: You can modify `p` later. + On the command line, options can be given as: - `-a` (flag) @@ -296,7 +297,7 @@ On the command line, options can be given as: - `--file filename` (space) - `--file=filename` (equals) -If allow_windows_style_options() is specified in the application or subcommand options can also be given as: +If `allow_windows_style_options()` is specified in the application or subcommand options can also be given as: - `/a` (flag) - `/f filename` (option) - `/long` (long flag) @@ -359,7 +360,7 @@ There are several options that are supported on the main app and subcommands. Th - `.name(name)`: Add or change the name. - `.callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. - `.allow_extras()`: Do not throw an error if extra arguments are left over. -- `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered. +- `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered. - `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app. - `.footer(message)`: Set text to appear at the bottom of the help string. - `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index eb617a0d..ba303b24 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -130,7 +130,7 @@ add_cli_exe(enum enum.cpp) add_test(NAME enum_pass COMMAND enum -l 1) add_test(NAME enum_fail COMMAND enum -l 4) set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION - "Could not convert: --level = 4") + "--level: 4 not in {0,1,2}") add_cli_exe(modhelp modhelp.cpp) add_test(NAME modhelp COMMAND modhelp -a test -h) diff --git a/examples/enum.cpp b/examples/enum.cpp index 5d404579..68c78f63 100644 --- a/examples/enum.cpp +++ b/examples/enum.cpp @@ -16,7 +16,8 @@ int main(int argc, char **argv) { CLI::App app; Level level; - app.add_set("-l,--level", level, {Level::High, Level::Medium, Level::Low}, "Level settings") + app.add_option("-l,--level", level, "Level settings") + ->check(CLI::IsMember({Level::High, Level::Medium, Level::Low})) ->type_name("enum/Level in {High=0, Medium=1, Low=2}"); CLI11_PARSE(app, argc, argv); diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 07ba44ee..6ab804e8 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -644,51 +644,31 @@ class App { } #endif - /// Add set of options (No default, temp reference, such as an inline set) + /// Add set of options (No default, temp reference, such as an inline set) DEPRECATED template Option *add_set(std::string option_name, T &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - bool retval = detail::lexical_cast(res[0], member); - if(!retval) - throw ConversionError(res[0], simple_name); - return std::find(std::begin(options), std::end(options), member) != std::end(options); - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{options}); return opt; } - /// Add set of options (No default, set can be changed afterwords - do not destroy the set) + /// Add set of options (No default, set can be changed afterwords - do not destroy the set) DEPRECATED template Option *add_mutable_set(std::string option_name, T &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - bool retval = detail::lexical_cast(res[0], member); - if(!retval) - throw ConversionError(res[0], simple_name); - return std::find(std::begin(options), std::end(options), member) != std::end(options); - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - opt->type_name_fn( - [&options]() { return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; }); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{&options}); return opt; } - /// Add set of options (with default, static set, such as an inline set) + /// Add set of options (with default, static set, such as an inline set) DEPRECATED template Option *add_set(std::string option_name, T &member, ///< The selected member of the set @@ -696,27 +676,12 @@ class App { std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - bool retval = detail::lexical_cast(res[0], member); - if(!retval) - throw ConversionError(res[0], simple_name); - return std::find(std::begin(options), std::end(options), member) != std::end(options); - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - if(defaulted) { - std::stringstream out; - out << member; - opt->default_str(out.str()); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{options}); return opt; } - /// Add set of options (with default, set can be changed afterwards - do not destroy the set) + /// Add set of options (with default, set can be changed afterwards - do not destroy the set) DEPRECATED template Option *add_mutable_set(std::string option_name, T &member, ///< The selected member of the set @@ -724,381 +689,166 @@ class App { std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - bool retval = detail::lexical_cast(res[0], member); - if(!retval) - throw ConversionError(res[0], simple_name); - return std::find(std::begin(options), std::end(options), member) != std::end(options); - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - opt->type_name_fn( - [&options]() { return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; }); - if(defaulted) { - std::stringstream out; - out << member; - opt->default_str(out.str()); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{&options}); return opt; } - /// Add set of options, string only, ignore case (no default, static set) + /// Add set of options, string only, ignore case (no default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) instead") Option *add_set_ignore_case(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::to_lower(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{options, CLI::ignore_case}); return opt; } /// Add set of options, string only, ignore case (no default, set can be changed afterwards - do not destroy the - /// set) + /// set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) with a (shared) pointer instead") Option *add_mutable_set_ignore_case(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::to_lower(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{&options, CLI::ignore_case}); return opt; } - /// Add set of options, string only, ignore case (default, static set) + /// Add set of options, string only, ignore case (default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case)) instead") Option *add_set_ignore_case(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::to_lower(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{options, CLI::ignore_case}); return opt; } /// Add set of options, string only, ignore case (default, set can be changed afterwards - do not destroy the set) + /// DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(...)) with a (shared) pointer instead") Option *add_mutable_set_ignore_case(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::to_lower(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{&options, CLI::ignore_case}); return opt; } - /// Add set of options, string only, ignore underscore (no default, static set) + /// Add set of options, string only, ignore underscore (no default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) instead") Option *add_set_ignore_underscore(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::remove_underscore(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::remove_underscore(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{options, CLI::ignore_underscore}); return opt; } /// Add set of options, string only, ignore underscore (no default, set can be changed afterwards - do not destroy - /// the set) + /// the set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead") Option *add_mutable_set_ignore_underscore(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::remove_underscore(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::remove_underscore(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{options, CLI::ignore_underscore}); return opt; } - /// Add set of options, string only, ignore underscore (default, static set) + /// Add set of options, string only, ignore underscore (default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) instead") Option *add_set_ignore_underscore(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::remove_underscore(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::remove_underscore(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{options, CLI::ignore_underscore}); return opt; } /// Add set of options, string only, ignore underscore (default, set can be changed afterwards - do not destroy the - /// set) + /// set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_underscore)) with a (shared) pointer instead") Option *add_mutable_set_ignore_underscore(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::remove_underscore(res[0]); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::remove_underscore(val) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{&options, CLI::ignore_underscore}); return opt; } - /// Add set of options, string only, ignore underscore and case (no default, static set) + /// Add set of options, string only, ignore underscore and case (no default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead") Option *add_set_ignore_case_underscore(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::to_lower(detail::remove_underscore(res[0])); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(detail::remove_underscore(val)) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{options, CLI::ignore_underscore, CLI::ignore_case}); return opt; } /// Add set of options, string only, ignore underscore and case (no default, set can be changed afterwards - do not - /// destroy the set) + /// destroy the set) DEPRECATED + CLI11_DEPRECATED( + "Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead") Option *add_mutable_set_ignore_case_underscore(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description = "") { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::to_lower(detail::remove_underscore(res[0])); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(detail::remove_underscore(val)) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), false); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - + Option *opt = add_option(option_name, member, std::move(description)); + opt->check(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case}); return opt; } - /// Add set of options, string only, ignore underscore and case (default, static set) + /// Add set of options, string only, ignore underscore and case (default, static set) DEPRECATED + CLI11_DEPRECATED("Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) instead") Option *add_set_ignore_case_underscore(std::string option_name, std::string &member, ///< The selected member of the set std::set options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { - member = detail::to_lower(detail::remove_underscore(res[0])); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(detail::remove_underscore(val)) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - std::string typeval = detail::type_name(); - typeval += " in {" + detail::join(options) + "}"; - opt->type_name(typeval); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{options, CLI::ignore_underscore, CLI::ignore_case}); return opt; } /// Add set of options, string only, ignore underscore and case (default, set can be changed afterwards - do not - /// destroy the set) + /// destroy the set) DEPRECATED + CLI11_DEPRECATED( + "Use ->check(CLI::IsMember(..., CLI::ignore_case, CLI::ignore_underscore)) with a (shared) pointer instead") Option *add_mutable_set_ignore_case_underscore(std::string option_name, std::string &member, ///< The selected member of the set const std::set &options, ///< The set of possibilities std::string description, bool defaulted) { - std::string simple_name = CLI::detail::split(option_name, ',').at(0); - CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) { - member = detail::to_lower(detail::remove_underscore(res[0])); - auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { - return detail::to_lower(detail::remove_underscore(val)) == member; - }); - if(iter == std::end(options)) - throw ConversionError(member, simple_name); - else { - member = *iter; - return true; - } - }; - - Option *opt = add_option(option_name, std::move(fun), std::move(description), defaulted); - opt->type_name_fn([&options]() { - return std::string(detail::type_name()) + " in {" + detail::join(options) + "}"; - }); - if(defaulted) { - opt->default_str(member); - } + Option *opt = add_option(option_name, member, std::move(description), defaulted); + opt->check(IsMember{&options, CLI::ignore_underscore, CLI::ignore_case}); return opt; } diff --git a/include/CLI/FormatterFwd.hpp b/include/CLI/FormatterFwd.hpp index 0344256c..ebaace64 100644 --- a/include/CLI/FormatterFwd.hpp +++ b/include/CLI/FormatterFwd.hpp @@ -49,7 +49,9 @@ class FormatterBase { FormatterBase() = default; FormatterBase(const FormatterBase &) = default; FormatterBase(FormatterBase &&) = default; - virtual ~FormatterBase() = default; + + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) /// This is the key method that puts together help virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; @@ -93,6 +95,9 @@ class FormatterLambda final : public FormatterBase { /// Create a FormatterLambda with a lambda function explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) + /// This will simply call the lambda function std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { return lambda_(app, name, mode); diff --git a/include/CLI/Option.hpp b/include/CLI/Option.hpp index 28bd260b..4c57af83 100644 --- a/include/CLI/Option.hpp +++ b/include/CLI/Option.hpp @@ -304,7 +304,9 @@ class Option : public OptionBase