Commit 3f4c165ea9db509ac07b9e71d3b11502c9f04310

Authored by Henry Fredrick Schreiner
1 parent 0ccd814a

Adding string based method for requires/excludes

CHANGELOG.md
1 1 ## Version 0.6
2 2  
  3 +* Added string versions of `->requires()` and `->excludes()` for consistency.
3 4 * Renamed protected members for internal consistency, grouped docs.
4 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 41 * Collect user feedback
42 42 * Ini configuration support is basic (long options only, no vector support), is more needed?
43 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 45 * Test "adding to cmake" method
47 46  
48 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 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 445 /// This does not run if any errors or help is thrown.
445 446 virtual void pre_callback() {}
446 447  
... ...
include/CLI/Option.hpp
... ... @@ -185,9 +185,19 @@ public:
185 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 201 requires(opt);
192 202 return requires(opt1, args...);
193 203 }
... ... @@ -200,9 +210,18 @@ public:
200 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 225 excludes(opt);
207 226 return excludes(opt1, args...);
208 227 }
... ...
tests/AppTest.cpp
... ... @@ -458,6 +458,30 @@ TEST_F(TApp, ExcludesFlags) {
458 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 486 TEST_F(TApp, RequiresMultiFlags) {
463 487 CLI::Option* opt1 = app.add_flag("--opt1");
... ... @@ -492,6 +516,39 @@ TEST_F(TApp, RequiresMultiFlags) {
492 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 552 TEST_F(TApp, RequiresChainedFlags) {
496 553 CLI::Option* opt1 = app.add_flag("--opt1");
497 554 CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1);
... ...