Commit 3f4c165ea9db509ac07b9e71d3b11502c9f04310
1 parent
0ccd814a
Adding string based method for requires/excludes
Showing
5 changed files
with
86 additions
and
9 deletions
CHANGELOG.md
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); | ... | ... |