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