Commit 3f4c165ea9db509ac07b9e71d3b11502c9f04310

Authored by Henry Fredrick Schreiner
1 parent 0ccd814a

Adding string based method for requires/excludes

CHANGELOG.md
1 ## Version 0.6 1 ## Version 0.6
2 2
  3 +* Added string versions of `->requires()` and `->excludes()` for consistency.
3 * Renamed protected members for internal consistency, grouped docs. 4 * Renamed protected members for internal consistency, grouped docs.
4 * Added the ability to add a number to `.require_subcommand()`. 5 * Added the ability to add a number to `.require_subcommand()`.
5 6
README.md
@@ -41,8 +41,7 @@ This library was built to supply the Application object for the GooFit CUDA/OMP @@ -41,8 +41,7 @@ This library was built to supply the Application object for the GooFit CUDA/OMP
41 * Collect user feedback 41 * Collect user feedback
42 * Ini configuration support is basic (long options only, no vector support), is more needed? 42 * Ini configuration support is basic (long options only, no vector support), is more needed?
43 * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object. 43 * Evaluate compatibility with [ROOT](https://root.cern.ch)'s TApplication object.
44 -* Expand `required`/`exluded` to take strings  
45 -* Document adding callback (maybe add C++14 only switch method?) 44 +* Document adding callback (maybe add C++14 only `add_switch` method?)
46 * Test "adding to cmake" method 45 * Test "adding to cmake" method
47 46
48 See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details. 47 See the [changelog](./CHANGELOG.md) or [GitHub releases](https://github.com/henryiii/CLI11/releases) for details.
include/CLI/App.hpp
@@ -440,7 +440,8 @@ public: @@ -440,7 +440,8 @@ public:
440 /// @name Extras for subclassing 440 /// @name Extras for subclassing
441 ///@{ 441 ///@{
442 442
443 - /// This allows subclasses to inject code before callbacks but after parse 443 + /// This allows subclasses to inject code before callbacks but after parse.
  444 + ///
444 /// This does not run if any errors or help is thrown. 445 /// This does not run if any errors or help is thrown.
445 virtual void pre_callback() {} 446 virtual void pre_callback() {}
446 447
include/CLI/Option.hpp
@@ -185,9 +185,19 @@ public: @@ -185,9 +185,19 @@ public:
185 return this; 185 return this;
186 } 186 }
187 187
188 - /// Any number supported  
189 - template<typename... ARG>  
190 - Option* requires(Option* opt, Option* opt1, ARG... args) { 188 + /// Can find a string if needed
  189 + template<typename T=App>
  190 + Option* requires(std::string opt_name) {
  191 + for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
  192 + if(opt.get() != this && opt->check_name(opt_name))
  193 + return requires(opt.get());
  194 + throw IncorrectConstruction("Option " + opt_name + " is not defined");
  195 +
  196 + }
  197 +
  198 + /// Any number supported, any mix of string and Opt
  199 + template<typename A, typename B, typename... ARG>
  200 + Option* requires(A opt, B opt1, ARG... args) {
191 requires(opt); 201 requires(opt);
192 return requires(opt1, args...); 202 return requires(opt1, args...);
193 } 203 }
@@ -200,9 +210,18 @@ public: @@ -200,9 +210,18 @@ public:
200 return this; 210 return this;
201 } 211 }
202 212
203 - /// Any number supported  
204 - template<typename... ARG>  
205 - Option* excludes(Option* opt, Option* opt1, ARG... args) { 213 + /// Can find a string if needed
  214 + template<typename T=App>
  215 + Option* excludes(std::string opt_name) {
  216 + for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
  217 + if(opt.get() != this && opt->check_name(opt_name))
  218 + return excludes(opt.get());
  219 + throw IncorrectConstruction("Option " + opt_name + " is not defined");
  220 +
  221 + }
  222 + /// Any number supported, any mix of string and Opt
  223 + template<typename A, typename B, typename... ARG>
  224 + Option* excludes(A opt, B opt1, ARG... args) {
206 excludes(opt); 225 excludes(opt);
207 return excludes(opt1, args...); 226 return excludes(opt1, args...);
208 } 227 }
tests/AppTest.cpp
@@ -458,6 +458,30 @@ TEST_F(TApp, ExcludesFlags) { @@ -458,6 +458,30 @@ TEST_F(TApp, ExcludesFlags) {
458 EXPECT_THROW(run(), CLI::ExcludesError); 458 EXPECT_THROW(run(), CLI::ExcludesError);
459 } 459 }
460 460
  461 +TEST_F(TApp, ExcludesMixedFlags) {
  462 + CLI::Option* opt1 = app.add_flag("--opt1");
  463 + app.add_flag("--opt2");
  464 + CLI::Option* opt3 = app.add_flag("--opt3");
  465 + app.add_flag("--no")->excludes(opt1, "--opt2", opt3);
  466 +
  467 + EXPECT_NO_THROW(run());
  468 +
  469 + app.reset();
  470 + args = {"--no"};
  471 + EXPECT_NO_THROW(run());
  472 +
  473 + app.reset();
  474 + args = {"--opt2"};
  475 + EXPECT_NO_THROW(run());
  476 +
  477 + app.reset();
  478 + args = {"--no", "--opt1"};
  479 + EXPECT_THROW(run(), CLI::ExcludesError);
  480 +
  481 + app.reset();
  482 + args = {"--no", "--opt2"};
  483 + EXPECT_THROW(run(), CLI::ExcludesError);
  484 +}
461 485
462 TEST_F(TApp, RequiresMultiFlags) { 486 TEST_F(TApp, RequiresMultiFlags) {
463 CLI::Option* opt1 = app.add_flag("--opt1"); 487 CLI::Option* opt1 = app.add_flag("--opt1");
@@ -492,6 +516,39 @@ TEST_F(TApp, RequiresMultiFlags) { @@ -492,6 +516,39 @@ TEST_F(TApp, RequiresMultiFlags) {
492 EXPECT_NO_THROW(run()); 516 EXPECT_NO_THROW(run());
493 } 517 }
494 518
  519 +TEST_F(TApp, RequiresMixedFlags) {
  520 + CLI::Option* opt1 = app.add_flag("--opt1");
  521 + CLI::Option* opt2 = app.add_flag("--opt2");
  522 + CLI::Option* opt3 = app.add_flag("--opt3");
  523 + app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3");
  524 +
  525 + EXPECT_NO_THROW(run());
  526 +
  527 + app.reset();
  528 + args = {"--opt1"};
  529 + EXPECT_NO_THROW(run());
  530 +
  531 + app.reset();
  532 + args = {"--opt2"};
  533 + EXPECT_NO_THROW(run());
  534 +
  535 + app.reset();
  536 + args = {"--optall"};
  537 + EXPECT_THROW(run(), CLI::RequiresError);
  538 +
  539 + app.reset();
  540 + args = {"--optall", "--opt1"};
  541 + EXPECT_THROW(run(), CLI::RequiresError);
  542 +
  543 + app.reset();
  544 + args = {"--optall", "--opt2", "--opt1"};
  545 + EXPECT_THROW(run(), CLI::RequiresError);
  546 +
  547 + app.reset();
  548 + args = {"--optall", "--opt1", "--opt2", "--opt3"};
  549 + EXPECT_NO_THROW(run());
  550 +}
  551 +
495 TEST_F(TApp, RequiresChainedFlags) { 552 TEST_F(TApp, RequiresChainedFlags) {
496 CLI::Option* opt1 = app.add_flag("--opt1"); 553 CLI::Option* opt1 = app.add_flag("--opt1");
497 CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1); 554 CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1);