diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index bae9019..a76b6f3 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -213,13 +213,14 @@ class App { } else 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 ::value, detail::enabler> = detail::dummy> Option *add_option(std::string name, T &variable, ///< The variable to set - std::string description = "", - bool defaulted = false) { + std::string description = "") { CLI::callback_t fun = [&variable](CLI::results_t res) { if(res.size() != 1) @@ -227,6 +228,24 @@ class App { return detail::lexical_cast(res[0], variable); }; + Option *opt = add_option(name, fun, description, false); + opt->set_custom_option(detail::type_name()); + return opt; + } + + /// Add option for non-vectors with a default print + template ::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) { + if(res.size() != 1) + return false; + return detail::lexical_cast(res[0], variable); + }; + Option *opt = add_option(name, fun, description, defaulted); opt->set_custom_option(detail::type_name()); if(defaulted) { @@ -236,13 +255,34 @@ class App { } return opt; } - + + /// Add option for vectors (no default) + template + Option *add_option(std::string name, + std::vector &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.size() > 0 && retval; + }; + + Option *opt = add_option(name, fun, description, false); + opt->set_custom_option(detail::type_name(), -1, true); + return opt; + } + /// Add option for vectors template Option *add_option(std::string name, std::vector &variable, ///< The variable vector to set - std::string description = "", - bool defaulted = false) { + std::string description, + bool defaulted) { CLI::callback_t fun = [&variable](CLI::results_t res) { bool retval = true; @@ -311,13 +351,12 @@ class App { return opt; } - /// Add set of options + /// Add set of options (No default) template Option *add_set(std::string name, T &member, ///< The selected member of the set std::set options, ///< The set of posibilities - std::string description = "", - bool defaulted = false) { + std::string description = "") { CLI::callback_t fun = [&member, options](CLI::results_t res) { if(res.size() != 1) { @@ -329,6 +368,31 @@ class App { 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(); + typeval += " in {" + detail::join(options) + "}"; + opt->set_custom_option(typeval); + return opt; + } + + /// Add set of options + template + Option *add_set(std::string name, + T &member, ///< The selected member of the set + std::set options, ///< The set of posibilities + std::string description, + bool defaulted) { + + 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, defaulted); std::string typeval = detail::type_name(); typeval += " in {" + detail::join(options) + "}"; @@ -341,12 +405,11 @@ class App { return opt; } - /// Add set of options, string only, ignore case + /// 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 options, ///< The set of posibilities - std::string description = "", - bool defaulted = false) { + std::string description = "") { CLI::callback_t fun = [&member, options](CLI::results_t res) { if(res.size() != 1) { @@ -364,6 +427,37 @@ class App { } }; + Option *opt = add_option(name, fun, description, false); + std::string typeval = detail::type_name(); + typeval += " in {" + detail::join(options) + "}"; + opt->set_custom_option(typeval); + + return opt; + } + + /// Add set of options, string only, ignore case + Option *add_set_ignore_case(std::string name, + std::string &member, ///< The selected member of the set + std::set options, ///< The set of posibilities + std::string description, + bool defaulted) { + + 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, defaulted); std::string typeval = detail::type_name(); typeval += " in {" + detail::join(options) + "}"; diff --git a/tests/AppTest.cpp b/tests/AppTest.cpp index 8e3922a..db8900d 100644 --- a/tests/AppTest.cpp +++ b/tests/AppTest.cpp @@ -217,7 +217,20 @@ TEST_F(TApp, EnumTest) { EXPECT_EQ(level, Level::Medium); } -// New style enums do not work, since << is not supported. Could be fixed without changing API by duplicating the `add_` methods with and without the extra flag. +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) { app.add_flag("-a")->required();