Commit 324a9c738decace7446bbae52c46a25d7a8c689a
1 parent
074d0339
Adding make_* options
Showing
2 changed files
with
140 additions
and
5 deletions
include/CLI.hpp
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | #include <algorithm> | 11 | #include <algorithm> |
| 12 | #include <sstream> | 12 | #include <sstream> |
| 13 | #include <type_traits> | 13 | #include <type_traits> |
| 14 | -#include <unordered_set> | 14 | +#include <set> |
| 15 | #include <iomanip> | 15 | #include <iomanip> |
| 16 | #include <numeric> | 16 | #include <numeric> |
| 17 | 17 | ||
| @@ -450,6 +450,10 @@ public: | @@ -450,6 +450,10 @@ public: | ||
| 450 | logit(subcommands.back()->name); | 450 | logit(subcommands.back()->name); |
| 451 | return subcommands.back().get(); | 451 | return subcommands.back().get(); |
| 452 | } | 452 | } |
| 453 | + | ||
| 454 | + | ||
| 455 | + //------------ ADD STYLE ---------// | ||
| 456 | + | ||
| 453 | /// Add an option, will automatically understand the type for common types. | 457 | /// Add an option, will automatically understand the type for common types. |
| 454 | /** To use, create a variable with the expected type, and pass it in after the name. | 458 | /** To use, create a variable with the expected type, and pass it in after the name. |
| 455 | * After start is called, you can use count to see if the value was passed, and | 459 | * After start is called, you can use count to see if the value was passed, and |
| @@ -563,7 +567,7 @@ public: | @@ -563,7 +567,7 @@ public: | ||
| 563 | Option* add_set( | 567 | Option* add_set( |
| 564 | std::string name, ///< The name, short,long | 568 | std::string name, ///< The name, short,long |
| 565 | T &member, ///< The selected member of the set | 569 | T &member, ///< The selected member of the set |
| 566 | - std::unordered_set<T> options, ///< The set of posibilities | 570 | + std::set<T> options, ///< The set of posibilities |
| 567 | std::string discription="", ///< Discription string | 571 | std::string discription="", ///< Discription string |
| 568 | Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) | 572 | Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) |
| 569 | ) { | 573 | ) { |
| @@ -581,14 +585,20 @@ public: | @@ -581,14 +585,20 @@ public: | ||
| 581 | bool retval = lexical_cast(res[0][0], member); | 585 | bool retval = lexical_cast(res[0][0], member); |
| 582 | if(!retval) | 586 | if(!retval) |
| 583 | return false; | 587 | return false; |
| 584 | - return std::find(std::begin(options), std::end(options), retval) != std::end(options); | 588 | + return std::find(std::begin(options), std::end(options), member) != std::end(options); |
| 585 | }; | 589 | }; |
| 586 | 590 | ||
| 587 | return add_option(name, fun, discription, opts); | 591 | return add_option(name, fun, discription, opts); |
| 588 | } | 592 | } |
| 589 | 593 | ||
| 594 | + | ||
| 595 | + | ||
| 596 | + | ||
| 597 | + //------------ MAKE STYLE ---------// | ||
| 598 | + | ||
| 590 | /// Prototype for new output style | 599 | /// Prototype for new output style |
| 591 | - template<typename T = std::string> | 600 | + template<typename T = std::string, |
| 601 | + enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> | ||
| 592 | Value<T> make_option( | 602 | Value<T> make_option( |
| 593 | std::string name, ///< The name, short,long | 603 | std::string name, ///< The name, short,long |
| 594 | std::string discription="", | 604 | std::string discription="", |
| @@ -611,7 +621,117 @@ public: | @@ -611,7 +621,117 @@ public: | ||
| 611 | add_option(name, fun, discription, opts); | 621 | add_option(name, fun, discription, opts); |
| 612 | return out; | 622 | return out; |
| 613 | } | 623 | } |
| 624 | + | ||
| 625 | + /// Prototype for new output style with default | ||
| 626 | + template<typename T = std::string, | ||
| 627 | + enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> | ||
| 628 | + Value<T> make_option( | ||
| 629 | + std::string name, ///< The name, short,long | ||
| 630 | + const T& default_value, | ||
| 631 | + std::string discription="", | ||
| 632 | + Combiner opts=VALIDATORS | ||
| 633 | + ) { | ||
| 634 | + | ||
| 635 | + Value<T> out(name); | ||
| 636 | + std::shared_ptr<std::unique_ptr<T>> ptr = out.value; | ||
| 637 | + ptr->reset(new T(default_value)); // resets the internal ptr | ||
| 638 | + | ||
| 639 | + CLI::callback_t fun = [ptr](CLI::results_t res){ | ||
| 640 | + if(res.size()!=1) { | ||
| 641 | + return false; | ||
| 642 | + } | ||
| 643 | + if(res[0].size()!=1) { | ||
| 644 | + return false; | ||
| 645 | + } | ||
| 646 | + ptr->reset(new T()); // resets the internal ptr | ||
| 647 | + return lexical_cast(res[0][0], **ptr); | ||
| 648 | + }; | ||
| 649 | + add_option(name, fun, discription, opts); | ||
| 650 | + return out; | ||
| 651 | + } | ||
| 614 | 652 | ||
| 653 | + /// Prototype for new output style | ||
| 654 | + template<typename T> | ||
| 655 | + Value<std::vector<T>> make_option( | ||
| 656 | + std::string name, ///< The name, short,long | ||
| 657 | + std::string discription="", | ||
| 658 | + Combiner opts=VALIDATORS | ||
| 659 | + ) { | ||
| 660 | + | ||
| 661 | + if(opts.num==0) | ||
| 662 | + throw IncorrectConstruction("Must have ARGS or be a vector."); | ||
| 663 | + | ||
| 664 | + Value<std::vector<T>> out(name); | ||
| 665 | + std::shared_ptr<std::unique_ptr<std::vector<T> >> ptr = out.value; | ||
| 666 | + | ||
| 667 | + CLI::callback_t fun = [ptr](CLI::results_t res){ | ||
| 668 | + ptr->reset(new std::vector<T>()); // resets the internal ptr | ||
| 669 | + bool retval = true; | ||
| 670 | + for(const auto &a : res) | ||
| 671 | + for(const auto &b : a) { | ||
| 672 | + (*ptr)->emplace_back(); | ||
| 673 | + retval &= lexical_cast(b, (*ptr)->back()); | ||
| 674 | + } | ||
| 675 | + return (*ptr)->size() > 0 && retval; | ||
| 676 | + }; | ||
| 677 | + add_option(name, fun, discription, opts); | ||
| 678 | + return out; | ||
| 679 | + } | ||
| 680 | + | ||
| 681 | + | ||
| 682 | + /// Prototype for new output style: flag | ||
| 683 | + Value<int> make_flag( | ||
| 684 | + std::string name, ///< The name, short,long | ||
| 685 | + std::string discription="" | ||
| 686 | + ) { | ||
| 687 | + | ||
| 688 | + Value<int> out(name); | ||
| 689 | + std::shared_ptr<std::unique_ptr<int>> ptr = out.value; | ||
| 690 | + ptr->reset(new int()); // resets the internal ptr | ||
| 691 | + **ptr = 0; | ||
| 692 | + | ||
| 693 | + CLI::callback_t fun = [ptr](CLI::results_t res){ | ||
| 694 | + **ptr = (int) res.size(); | ||
| 695 | + return true; | ||
| 696 | + }; | ||
| 697 | + add_option(name, fun, discription, NOTHING); | ||
| 698 | + return out; | ||
| 699 | + } | ||
| 700 | + | ||
| 701 | + /// Add set of options | ||
| 702 | + template<typename T> | ||
| 703 | + Value<T> make_set( | ||
| 704 | + std::string name, ///< The name, short,long | ||
| 705 | + std::set<T> options, ///< The set of posibilities | ||
| 706 | + std::string discription="", ///< Discription string | ||
| 707 | + Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) | ||
| 708 | + ) { | ||
| 709 | + | ||
| 710 | + Value<T> out(name); | ||
| 711 | + std::shared_ptr<std::unique_ptr<T>> ptr = out.value; | ||
| 712 | + | ||
| 713 | + if(opts.num!=1) | ||
| 714 | + throw IncorrectConstruction("Must have ARGS(1)."); | ||
| 715 | + | ||
| 716 | + CLI::callback_t fun = [ptr, options](CLI::results_t res){ | ||
| 717 | + if(res.size()!=1) { | ||
| 718 | + return false; | ||
| 719 | + } | ||
| 720 | + if(res[0].size()!=1) { | ||
| 721 | + return false; | ||
| 722 | + } | ||
| 723 | + ptr->reset(new T()); | ||
| 724 | + bool retval = lexical_cast(res[0][0], **ptr); | ||
| 725 | + if(!retval) | ||
| 726 | + return false; | ||
| 727 | + return std::find(std::begin(options), std::end(options), **ptr) != std::end(options); | ||
| 728 | + }; | ||
| 729 | + | ||
| 730 | + add_option(name, fun, discription, opts); | ||
| 731 | + return out; | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + | ||
| 615 | 735 | ||
| 616 | /// Parses the command line - throws errors | 736 | /// Parses the command line - throws errors |
| 617 | void parse(int argc, char **argv) { | 737 | void parse(int argc, char **argv) { |
tests/CLITest.cpp
| @@ -249,6 +249,22 @@ TEST_F(TApp, FileExists) { | @@ -249,6 +249,22 @@ TEST_F(TApp, FileExists) { | ||
| 249 | EXPECT_FALSE(CLI::_ExistingFile(myfile)); | 249 | EXPECT_FALSE(CLI::_ExistingFile(myfile)); |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | +TEST_F(TApp, InSet) { | ||
| 253 | + | ||
| 254 | + std::string choice; | ||
| 255 | + app.add_set("q,quick", choice, {"one", "two", "three"}); | ||
| 256 | + | ||
| 257 | + args = {"--quick", "two"}; | ||
| 258 | + | ||
| 259 | + EXPECT_NO_THROW(run()); | ||
| 260 | + EXPECT_EQ("two", choice); | ||
| 261 | + | ||
| 262 | + app.reset(); | ||
| 263 | + | ||
| 264 | + args = {"--quick", "four"}; | ||
| 265 | + EXPECT_THROW(run(), CLI::ParseError); | ||
| 266 | +} | ||
| 267 | + | ||
| 252 | TEST_F(TApp, VectorFixedString) { | 268 | TEST_F(TApp, VectorFixedString) { |
| 253 | std::vector<std::string> strvec; | 269 | std::vector<std::string> strvec; |
| 254 | std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; | 270 | std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; |
| @@ -366,7 +382,6 @@ TEST_F(TAppValue, OneString) { | @@ -366,7 +382,6 @@ TEST_F(TAppValue, OneString) { | ||
| 366 | 382 | ||
| 367 | } | 383 | } |
| 368 | 384 | ||
| 369 | -// TODO: Add vector arguments | ||
| 370 | // TODO: Maybe add function to call on subcommand parse? Stashed. | 385 | // TODO: Maybe add function to call on subcommand parse? Stashed. |
| 371 | // TODO: Check help output | 386 | // TODO: Check help output |
| 372 | // TODO: Add default/type info to help | 387 | // TODO: Add default/type info to help |