diff --git a/CHANGELOG.md b/CHANGELOG.md index 19732f3c..94bad725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Version 0.6 +* Added string versions of `->requires()` and `->excludes()` for consistency. * Renamed protected members for internal consistency, grouped docs. * Added the ability to add a number to `.require_subcommand()`. diff --git a/README.md b/README.md index 7be391fc..406915df 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,7 @@ This library was built to supply the Application object for the GooFit CUDA/OMP * Collect user feedback * Ini configuration support is basic (long options only, no vector support), is more needed? * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object. -* Expand `required`/`exluded` to take strings -* Document adding callback (maybe add C++14 only switch method?) +* Document adding callback (maybe add C++14 only `add_switch` method?) * Test "adding to cmake" method See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details. diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index d3e53667..cd09dcef 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -440,7 +440,8 @@ public: /// @name Extras for subclassing ///@{ - /// This allows subclasses to inject code before callbacks but after parse + /// This allows subclasses to inject code before callbacks but after parse. + /// /// This does not run if any errors or help is thrown. virtual void pre_callback() {} diff --git a/include/CLI/Option.hpp b/include/CLI/Option.hpp index 99192129..345819b8 100644 --- a/include/CLI/Option.hpp +++ b/include/CLI/Option.hpp @@ -185,9 +185,19 @@ public: return this; } - /// Any number supported - template - Option* requires(Option* opt, Option* opt1, ARG... args) { + /// Can find a string if needed + template + Option* requires(std::string opt_name) { + for(const Option_p& opt : dynamic_cast(parent_)->options_) + if(opt.get() != this && opt->check_name(opt_name)) + return requires(opt.get()); + throw IncorrectConstruction("Option " + opt_name + " is not defined"); + + } + + /// Any number supported, any mix of string and Opt + template + Option* requires(A opt, B opt1, ARG... args) { requires(opt); return requires(opt1, args...); } @@ -200,9 +210,18 @@ public: return this; } - /// Any number supported - template - Option* excludes(Option* opt, Option* opt1, ARG... args) { + /// Can find a string if needed + template + Option* excludes(std::string opt_name) { + for(const Option_p& opt : dynamic_cast(parent_)->options_) + if(opt.get() != this && opt->check_name(opt_name)) + return excludes(opt.get()); + throw IncorrectConstruction("Option " + opt_name + " is not defined"); + + } + /// Any number supported, any mix of string and Opt + template + Option* excludes(A opt, B opt1, ARG... args) { excludes(opt); return excludes(opt1, args...); } diff --git a/tests/AppTest.cpp b/tests/AppTest.cpp index 643b6ba2..3e65edf2 100644 --- a/tests/AppTest.cpp +++ b/tests/AppTest.cpp @@ -458,6 +458,30 @@ TEST_F(TApp, ExcludesFlags) { EXPECT_THROW(run(), CLI::ExcludesError); } +TEST_F(TApp, ExcludesMixedFlags) { + CLI::Option* opt1 = app.add_flag("--opt1"); + app.add_flag("--opt2"); + CLI::Option* opt3 = app.add_flag("--opt3"); + app.add_flag("--no")->excludes(opt1, "--opt2", opt3); + + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--no"}; + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--opt2"}; + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--no", "--opt1"}; + EXPECT_THROW(run(), CLI::ExcludesError); + + app.reset(); + args = {"--no", "--opt2"}; + EXPECT_THROW(run(), CLI::ExcludesError); +} TEST_F(TApp, RequiresMultiFlags) { CLI::Option* opt1 = app.add_flag("--opt1"); @@ -492,6 +516,39 @@ TEST_F(TApp, RequiresMultiFlags) { EXPECT_NO_THROW(run()); } +TEST_F(TApp, RequiresMixedFlags) { + CLI::Option* opt1 = app.add_flag("--opt1"); + CLI::Option* opt2 = app.add_flag("--opt2"); + CLI::Option* opt3 = app.add_flag("--opt3"); + app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3"); + + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--opt1"}; + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--opt2"}; + EXPECT_NO_THROW(run()); + + app.reset(); + args = {"--optall"}; + EXPECT_THROW(run(), CLI::RequiresError); + + app.reset(); + args = {"--optall", "--opt1"}; + EXPECT_THROW(run(), CLI::RequiresError); + + app.reset(); + args = {"--optall", "--opt2", "--opt1"}; + EXPECT_THROW(run(), CLI::RequiresError); + + app.reset(); + args = {"--optall", "--opt1", "--opt2", "--opt3"}; + EXPECT_NO_THROW(run()); +} + TEST_F(TApp, RequiresChainedFlags) { CLI::Option* opt1 = app.add_flag("--opt1"); CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1);