mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-05-08 15:53:51 +00:00
commit
5b714bbfe4
@ -1,10 +1,11 @@
|
|||||||
## Version 1.1 (in progress)
|
## Version 1.1 (in progress)
|
||||||
|
|
||||||
|
* Added simple support for enumerations, allow non-printable objects [#12](https://github.com/CLIUtils/CLI11/issues/12)
|
||||||
* Added `app.parse_order()` with original parse order ([#13](https://github.com/CLIUtils/CLI11/issues/13), [#16](https://github.com/CLIUtils/CLI11/pull/16))
|
* Added `app.parse_order()` with original parse order ([#13](https://github.com/CLIUtils/CLI11/issues/13), [#16](https://github.com/CLIUtils/CLI11/pull/16))
|
||||||
* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. ([#8](https://github.com/CLIUtils/CLI11/issues/8), [#17](https://github.com/CLIUtils/CLI11/pull/17))
|
* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. ([#8](https://github.com/CLIUtils/CLI11/issues/8), [#17](https://github.com/CLIUtils/CLI11/pull/17))
|
||||||
* Removed Windows error ([#10](https://github.com/CLIUtils/CLI11/issues/10), [#20](https://github.com/CLIUtils/CLI11/pull/20))
|
* Removed Windows error ([#10](https://github.com/CLIUtils/CLI11/issues/10), [#20](https://github.com/CLIUtils/CLI11/pull/20))
|
||||||
* Some improvements to CMake, detect Python and no dependencies on Python 2 (like Python 3) ([#18](https://github.com/CLIUtils/CLI11/issues/18), [#21](https://github.com/CLIUtils/CLI11/pull/21))
|
* Some improvements to CMake, detect Python and no dependencies on Python 2 (like Python 3) ([#18](https://github.com/CLIUtils/CLI11/issues/18), [#21](https://github.com/CLIUtils/CLI11/pull/21))
|
||||||
|
|
||||||
|
|
||||||
## Version 1.0
|
## Version 1.0
|
||||||
* Cleanup using `clang-tidy` and `clang-format`
|
* Cleanup using `clang-tidy` and `clang-format`
|
||||||
* Small improvements to Timers, easier to subclass Error
|
* Small improvements to Timers, easier to subclass Error
|
||||||
|
@ -19,3 +19,4 @@ add_cli_exe(subcommands subcommands.cpp)
|
|||||||
add_cli_exe(groups groups.cpp)
|
add_cli_exe(groups groups.cpp)
|
||||||
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
||||||
add_cli_exe(prefix_command prefix_command.cpp)
|
add_cli_exe(prefix_command prefix_command.cpp)
|
||||||
|
add_cli_exe(enum enum.cpp)
|
||||||
|
23
examples/enum.cpp
Normal file
23
examples/enum.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
|
||||||
|
enum Level : std::int32_t {
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Low
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
CLI::App app;
|
||||||
|
|
||||||
|
Level level;
|
||||||
|
app.add_set("-l,--level", level, {High, Medium, Low}, "Level settings")
|
||||||
|
->set_type_name("enum/Level in {High=0, Medium=1, Low=2}");
|
||||||
|
|
||||||
|
try {
|
||||||
|
app.parse(argc, argv);
|
||||||
|
} catch (CLI::Error const& e) {
|
||||||
|
app.exit(e);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -226,12 +226,31 @@ class App {
|
|||||||
throw OptionAlreadyAdded(myopt.get_name());
|
throw OptionAlreadyAdded(myopt.get_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add option for non-vectors
|
|
||||||
|
|
||||||
|
/// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
|
||||||
template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
|
||||||
Option *add_option(std::string name,
|
Option *add_option(std::string name,
|
||||||
T &variable, ///< The variable to set
|
T &variable, ///< The variable to set
|
||||||
std::string description = "",
|
std::string description = "") {
|
||||||
bool defaulted = false) {
|
|
||||||
|
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
||||||
|
if(res.size() != 1)
|
||||||
|
return false;
|
||||||
|
return detail::lexical_cast(res[0], variable);
|
||||||
|
};
|
||||||
|
|
||||||
|
Option *opt = add_option(name, fun, description, false);
|
||||||
|
opt->set_custom_option(detail::type_name<T>());
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add option for non-vectors with a default print
|
||||||
|
template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
|
||||||
|
Option *add_option(std::string name,
|
||||||
|
T &variable, ///< The variable to set
|
||||||
|
std::string description,
|
||||||
|
bool defaulted) {
|
||||||
|
|
||||||
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
||||||
if(res.size() != 1)
|
if(res.size() != 1)
|
||||||
@ -249,12 +268,33 @@ class App {
|
|||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add option for vectors (no default)
|
||||||
|
template <typename T>
|
||||||
|
Option *add_option(std::string name,
|
||||||
|
std::vector<T> &variable, ///< The variable vector to set
|
||||||
|
std::string description = "") {
|
||||||
|
|
||||||
|
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
||||||
|
bool retval = true;
|
||||||
|
variable.clear();
|
||||||
|
for(const auto &a : res) {
|
||||||
|
variable.emplace_back();
|
||||||
|
retval &= detail::lexical_cast(a, variable.back());
|
||||||
|
}
|
||||||
|
return (!variable.empty()) && retval;
|
||||||
|
};
|
||||||
|
|
||||||
|
Option *opt = add_option(name, fun, description, false);
|
||||||
|
opt->set_custom_option(detail::type_name<T>(), -1, true);
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
/// Add option for vectors
|
/// Add option for vectors
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Option *add_option(std::string name,
|
Option *add_option(std::string name,
|
||||||
std::vector<T> &variable, ///< The variable vector to set
|
std::vector<T> &variable, ///< The variable vector to set
|
||||||
std::string description = "",
|
std::string description,
|
||||||
bool defaulted = false) {
|
bool defaulted) {
|
||||||
|
|
||||||
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
||||||
bool retval = true;
|
bool retval = true;
|
||||||
@ -323,13 +363,37 @@ class App {
|
|||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add set of options (No default)
|
||||||
|
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
|
||||||
|
std::string description = "") {
|
||||||
|
|
||||||
|
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
||||||
|
if(res.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool retval = detail::lexical_cast(res[0], member);
|
||||||
|
if(!retval)
|
||||||
|
return false;
|
||||||
|
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
|
/// Add set of options
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Option *add_set(std::string name,
|
Option *add_set(std::string name,
|
||||||
T &member, ///< The selected member of the set
|
T &member, ///< The selected member of the set
|
||||||
std::set<T> options, ///< The set of posibilities
|
std::set<T> options, ///< The set of posibilities
|
||||||
std::string description = "",
|
std::string description,
|
||||||
bool defaulted = false) {
|
bool defaulted) {
|
||||||
|
|
||||||
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
||||||
if(res.size() != 1) {
|
if(res.size() != 1) {
|
||||||
@ -353,12 +417,42 @@ class App {
|
|||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add set of options, string only, ignore case (no default)
|
||||||
|
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 description = "") {
|
||||||
|
|
||||||
|
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
||||||
|
if(res.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
return false;
|
||||||
|
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
|
/// Add set of options, string only, ignore case
|
||||||
Option *add_set_ignore_case(std::string name,
|
Option *add_set_ignore_case(std::string name,
|
||||||
std::string &member, ///< The selected member of the set
|
std::string &member, ///< The selected member of the set
|
||||||
std::set<std::string> options, ///< The set of posibilities
|
std::set<std::string> options, ///< The set of posibilities
|
||||||
std::string description = "",
|
std::string description,
|
||||||
bool defaulted = false) {
|
bool defaulted) {
|
||||||
|
|
||||||
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
CLI::callback_t fun = [&member, options](CLI::results_t res) {
|
||||||
if(res.size() != 1) {
|
if(res.size() != 1) {
|
||||||
|
@ -74,8 +74,10 @@ constexpr const char *type_name() {
|
|||||||
|
|
||||||
// Lexical cast
|
// Lexical cast
|
||||||
|
|
||||||
/// Integers
|
/// Integers / enums
|
||||||
template <typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<std::is_integral<T>::value
|
||||||
|
|| std::is_enum<T>::value
|
||||||
|
, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(std::string input, T &output) {
|
bool lexical_cast(std::string input, T &output) {
|
||||||
try {
|
try {
|
||||||
output = static_cast<T>(std::stoll(input));
|
output = static_cast<T>(std::stoll(input));
|
||||||
@ -103,7 +105,9 @@ bool lexical_cast(std::string input, T &output) {
|
|||||||
/// String and similar
|
/// String and similar
|
||||||
template <
|
template <
|
||||||
typename T,
|
typename T,
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value, detail::enabler> = detail::dummy>
|
enable_if_t<!std::is_floating_point<T>::value
|
||||||
|
&& !std::is_integral<T>::value
|
||||||
|
&& !std::is_enum<T>::value, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(std::string input, T &output) {
|
bool lexical_cast(std::string input, T &output) {
|
||||||
output = input;
|
output = input;
|
||||||
return true;
|
return true;
|
||||||
|
@ -203,6 +203,35 @@ TEST_F(TApp, DefaultOpts) {
|
|||||||
EXPECT_EQ("9", s);
|
EXPECT_EQ("9", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, EnumTest) {
|
||||||
|
enum Level : std::int32_t {
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Low
|
||||||
|
};
|
||||||
|
Level level = Level::Low;
|
||||||
|
app.add_option("--level", level);
|
||||||
|
|
||||||
|
args = {"--level", "1"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(level, Level::Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, NewEnumTest) {
|
||||||
|
enum class Level2 : std::int32_t {
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Low
|
||||||
|
};
|
||||||
|
Level2 level = Level2::Low;
|
||||||
|
app.add_option("--level", level);
|
||||||
|
|
||||||
|
args = {"--level", "1"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(level, Level2::Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_F(TApp, RequiredFlags) {
|
TEST_F(TApp, RequiredFlags) {
|
||||||
app.add_flag("-a")->required();
|
app.add_flag("-a")->required();
|
||||||
app.add_flag("-b")->mandatory(); // Alternate term
|
app.add_flag("-b")->mandatory(); // Alternate term
|
||||||
@ -391,6 +420,47 @@ TEST_F(TApp, InSet) {
|
|||||||
EXPECT_THROW(run(), CLI::ConversionError);
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, InSetWithDefault) {
|
||||||
|
|
||||||
|
std::string choice = "one";
|
||||||
|
app.add_set("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||||
|
|
||||||
|
run();
|
||||||
|
EXPECT_EQ("one", choice);
|
||||||
|
app.reset();
|
||||||
|
|
||||||
|
args = {"--quick", "two"};
|
||||||
|
|
||||||
|
run();
|
||||||
|
EXPECT_EQ("two", choice);
|
||||||
|
|
||||||
|
app.reset();
|
||||||
|
|
||||||
|
args = {"--quick", "four"};
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TApp, InCaselessSetWithDefault) {
|
||||||
|
|
||||||
|
std::string choice = "one";
|
||||||
|
app.add_set_ignore_case("-q,--quick", choice, {"one", "two", "three"}, "", true);
|
||||||
|
|
||||||
|
run();
|
||||||
|
EXPECT_EQ("one", choice);
|
||||||
|
app.reset();
|
||||||
|
|
||||||
|
args = {"--quick", "tWo"};
|
||||||
|
|
||||||
|
run();
|
||||||
|
EXPECT_EQ("two", choice);
|
||||||
|
|
||||||
|
app.reset();
|
||||||
|
|
||||||
|
args = {"--quick", "four"};
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, InIntSet) {
|
TEST_F(TApp, InIntSet) {
|
||||||
|
|
||||||
int choice;
|
int choice;
|
||||||
@ -462,6 +532,20 @@ TEST_F(TApp, VectorFixedString) {
|
|||||||
EXPECT_EQ(answer, strvec);
|
EXPECT_EQ(answer, strvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TApp, VectorDefaultedFixedString) {
|
||||||
|
std::vector<std::string> strvec{"one"};
|
||||||
|
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||||
|
|
||||||
|
CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3);
|
||||||
|
EXPECT_EQ(3, opt->get_expected());
|
||||||
|
|
||||||
|
args = {"--string", "mystring", "mystring2", "mystring3"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ((size_t)3, app.count("--string"));
|
||||||
|
EXPECT_EQ(answer, strvec);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, VectorUnlimString) {
|
TEST_F(TApp, VectorUnlimString) {
|
||||||
std::vector<std::string> strvec;
|
std::vector<std::string> strvec;
|
||||||
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||||
@ -808,3 +892,43 @@ TEST_F(TApp, CheckSubcomFail) {
|
|||||||
|
|
||||||
EXPECT_THROW(CLI::detail::AppFriend::parse_subcommand(&app, args), CLI::HorribleError);
|
EXPECT_THROW(CLI::detail::AppFriend::parse_subcommand(&app, args), CLI::HorribleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Added to test defaults on dual method
|
||||||
|
TEST_F(TApp, OptionWithDefaults) {
|
||||||
|
int someint=2;
|
||||||
|
app.add_option("-a", someint, "", true);
|
||||||
|
|
||||||
|
args = {"-a1", "-a2"};
|
||||||
|
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added to test defaults on dual method
|
||||||
|
TEST_F(TApp, SetWithDefaults) {
|
||||||
|
int someint=2;
|
||||||
|
app.add_set("-a", someint, {1,2,3,4}, "", true);
|
||||||
|
|
||||||
|
args = {"-a1", "-a2"};
|
||||||
|
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added to test defaults on dual method
|
||||||
|
TEST_F(TApp, SetWithDefaultsConversion) {
|
||||||
|
int someint=2;
|
||||||
|
app.add_set("-a", someint, {1,2,3,4}, "", true);
|
||||||
|
|
||||||
|
args = {"-a", "hi"};
|
||||||
|
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added to test defaults on dual method
|
||||||
|
TEST_F(TApp, SetWithDefaultsIC) {
|
||||||
|
std::string someint="ho";
|
||||||
|
app.add_set_ignore_case("-a", someint, {"Hi", "Ho"}, "", true);
|
||||||
|
|
||||||
|
args = {"-aHi", "-aHo"};
|
||||||
|
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user