mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
Sets by reference (#114)
* Adding const & access to sets * Adding set reference option * One missing line in coverage
This commit is contained in:
parent
af2ed66d6e
commit
3917b1ab59
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,6 +1,6 @@
|
||||
### Version 1.6: Formatting
|
||||
|
||||
Added a new formatting system. You can now set the formatter on Apps.
|
||||
Added a new formatting system. [#109] You can now set the formatter on Apps.
|
||||
|
||||
* Added `CLI::Formatter` and `formatter` slot for apps, inherited.
|
||||
* Added `help_all` support (not added by default)
|
||||
@ -18,11 +18,18 @@ Changes to the help system (most normal users will not notice this):
|
||||
* `format_help` can now be chained
|
||||
|
||||
|
||||
Other small changes:
|
||||
Other changes:
|
||||
|
||||
* Testing (only) now uses submodules.
|
||||
* Removed `requires` in favor of `needs` (deprecated in last version)
|
||||
* Better CMake policy handling
|
||||
* Using `add_set` will now capture L-values for sets, allowing further modification [#113]
|
||||
* Testing (only) now uses submodules. [#111]
|
||||
* Removed `requires` in favor of `needs` (deprecated in last version) [#112]
|
||||
* Better CMake policy handling [#110]
|
||||
|
||||
[#109]: https://github.com/CLIUtils/CLI11/pull/109
|
||||
[#110]: https://github.com/CLIUtils/CLI11/pull/110
|
||||
[#111]: https://github.com/CLIUtils/CLI11/pull/111
|
||||
[#112]: https://github.com/CLIUtils/CLI11/pull/112
|
||||
[#113]: https://github.com/CLIUtils/CLI11/issues/113
|
||||
|
||||
### Version 1.5.3: Compiler compatibility
|
||||
This version fixes older AppleClang compilers by removing the optimization for casting. The minimum version of Boost Optional supported has been clarified to be 1.58. CUDA 7.0 NVCC is now supported.
|
||||
@ -45,7 +52,7 @@ This patch release adds better access to the App progromatically, to assist with
|
||||
|
||||
[#102]: https://github.com/CLIUtils/CLI11/issues/102
|
||||
[#104]: https://github.com/CLIUtils/CLI11/pull/104
|
||||
[#105]: https://github.com/CLIUtils/CLI11/issues/105
|
||||
[#105]: https://github.com/CLIUtils/CLI11/pull/105
|
||||
[#106]: https://github.com/CLIUtils/CLI11/pull/106
|
||||
|
||||
|
||||
|
@ -163,7 +163,7 @@ app.add_set_ignore_case(... // String only
|
||||
App* subcom = app.add_subcommand(name, discription);
|
||||
```
|
||||
|
||||
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.
|
||||
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; you can add an existing set if you need to modify the set later, or you can use an initializer list.
|
||||
|
||||
On a C++14 compiler, you can pass a callback function directly to `.add_flag`, while in C++11 mode you'll need to use `.add_flag_function` if you want a callback function. The function will be given the number of times the flag was passed. You can throw a relevant `CLI::ParseError` to signal a failure.
|
||||
|
||||
|
@ -500,11 +500,11 @@ class App {
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Add set of options (No default)
|
||||
/// Add set of options (No default, temp refernce, such as an inline set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
std::set<T> options, ///< The set of possibilities
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &&options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
@ -522,11 +522,33 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options
|
||||
/// Add set of options (No default, non-temp refernce, such as an existing set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
std::set<T> options, ///< The set of posibilities
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(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(name, fun, description, false);
|
||||
std::string typeval = detail::type_name<T>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->set_custom_option(typeval);
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options (with default, R value, such as an inline set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &&options, ///< The set of posibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -550,10 +572,38 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (no default)
|
||||
/// Add set of options (with default, L value refernce, such as an existing set)
|
||||
template <typename T>
|
||||
Option *add_set(std::string name,
|
||||
T &member, ///< The selected member of the set
|
||||
const std::set<T> &options, ///< The set of posibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(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(name, fun, description, defaulted);
|
||||
std::string typeval = detail::type_name<T>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->set_custom_option(typeval);
|
||||
if(defaulted) {
|
||||
std::stringstream out;
|
||||
out << member;
|
||||
opt->set_default_str(out.str());
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (no default, R value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of possibilities
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(name, ',').at(0);
|
||||
@ -578,10 +628,38 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case
|
||||
/// Add set of options, string only, ignore case (no default, L value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
std::set<std::string> options, ///< The set of posibilities
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of possibilities
|
||||
std::string description = "") {
|
||||
|
||||
std::string simple_name = CLI::detail::split(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(name, fun, description, false);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->set_custom_option(typeval);
|
||||
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (default, R value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &&options, ///< The set of posibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
@ -609,6 +687,37 @@ class App {
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add set of options, string only, ignore case (default, L value)
|
||||
Option *add_set_ignore_case(std::string name,
|
||||
std::string &member, ///< The selected member of the set
|
||||
const std::set<std::string> &options, ///< The set of posibilities
|
||||
std::string description,
|
||||
bool defaulted) {
|
||||
|
||||
std::string simple_name = CLI::detail::split(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(name, fun, description, defaulted);
|
||||
std::string typeval = detail::type_name<std::string>();
|
||||
typeval += " in {" + detail::join(options) + "}";
|
||||
opt->set_custom_option(typeval);
|
||||
if(defaulted) {
|
||||
opt->set_default_str(member);
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
|
||||
/// Add a complex number
|
||||
template <typename T>
|
||||
Option *add_complex(std::string name,
|
||||
|
@ -992,6 +992,21 @@ TEST_F(TApp, FailSet) {
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, FailLValueSet) {
|
||||
|
||||
int choice;
|
||||
std::set<int> vals{1, 2, 3};
|
||||
app.add_set("-q,--quick", choice, vals);
|
||||
app.add_set("-s,--slow", choice, vals, "", true);
|
||||
|
||||
args = {"--quick=hello"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
app.reset();
|
||||
args = {"--slow=hello"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreCase) {
|
||||
|
||||
std::string choice;
|
||||
@ -1480,3 +1495,72 @@ TEST_F(TApp, CustomDoubleOption) {
|
||||
EXPECT_EQ(custom_opt.first, 12);
|
||||
EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
|
||||
}
|
||||
|
||||
// #113
|
||||
TEST_F(TApp, AddRemoveSetItems) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_set("--type1", type1, items);
|
||||
app.add_set("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPE1", "--type2", "TYPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
app.reset();
|
||||
args = {"--type1", "TYPE6", "--type2", "TYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
app.reset();
|
||||
args = {"--type1", "TYPE1"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
app.reset();
|
||||
args = {"--type2", "TYPE2"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
||||
TEST_F(TApp, AddRemoveSetItemsNoCase) {
|
||||
std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
|
||||
|
||||
std::string type1, type2;
|
||||
app.add_set_ignore_case("--type1", type1, items);
|
||||
app.add_set_ignore_case("--type2", type2, items, "", true);
|
||||
|
||||
args = {"--type1", "TYPe1", "--type2", "TyPE2"};
|
||||
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE1");
|
||||
EXPECT_EQ(type2, "TYPE2");
|
||||
|
||||
items.insert("TYPE6");
|
||||
items.insert("TYPE7");
|
||||
|
||||
items.erase("TYPE1");
|
||||
items.erase("TYPE2");
|
||||
|
||||
app.reset();
|
||||
args = {"--type1", "TyPE6", "--type2", "tYPE7"};
|
||||
run();
|
||||
EXPECT_EQ(type1, "TYPE6");
|
||||
EXPECT_EQ(type2, "TYPE7");
|
||||
|
||||
app.reset();
|
||||
args = {"--type1", "TYPe1"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
|
||||
app.reset();
|
||||
args = {"--type2", "TYpE2"};
|
||||
EXPECT_THROW(run(), CLI::ConversionError);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user