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 | 11 | #include <algorithm> |
| 12 | 12 | #include <sstream> |
| 13 | 13 | #include <type_traits> |
| 14 | -#include <unordered_set> | |
| 14 | +#include <set> | |
| 15 | 15 | #include <iomanip> |
| 16 | 16 | #include <numeric> |
| 17 | 17 | |
| ... | ... | @@ -450,6 +450,10 @@ public: |
| 450 | 450 | logit(subcommands.back()->name); |
| 451 | 451 | return subcommands.back().get(); |
| 452 | 452 | } |
| 453 | + | |
| 454 | + | |
| 455 | + //------------ ADD STYLE ---------// | |
| 456 | + | |
| 453 | 457 | /// Add an option, will automatically understand the type for common types. |
| 454 | 458 | /** To use, create a variable with the expected type, and pass it in after the name. |
| 455 | 459 | * After start is called, you can use count to see if the value was passed, and |
| ... | ... | @@ -563,7 +567,7 @@ public: |
| 563 | 567 | Option* add_set( |
| 564 | 568 | std::string name, ///< The name, short,long |
| 565 | 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 | 571 | std::string discription="", ///< Discription string |
| 568 | 572 | Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) |
| 569 | 573 | ) { |
| ... | ... | @@ -581,14 +585,20 @@ public: |
| 581 | 585 | bool retval = lexical_cast(res[0][0], member); |
| 582 | 586 | if(!retval) |
| 583 | 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 | 591 | return add_option(name, fun, discription, opts); |
| 588 | 592 | } |
| 589 | 593 | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + //------------ MAKE STYLE ---------// | |
| 598 | + | |
| 590 | 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 | 602 | Value<T> make_option( |
| 593 | 603 | std::string name, ///< The name, short,long |
| 594 | 604 | std::string discription="", |
| ... | ... | @@ -611,7 +621,117 @@ public: |
| 611 | 621 | add_option(name, fun, discription, opts); |
| 612 | 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 | 736 | /// Parses the command line - throws errors |
| 617 | 737 | void parse(int argc, char **argv) { | ... | ... |
tests/CLITest.cpp
| ... | ... | @@ -249,6 +249,22 @@ TEST_F(TApp, FileExists) { |
| 249 | 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 | 268 | TEST_F(TApp, VectorFixedString) { |
| 253 | 269 | std::vector<std::string> strvec; |
| 254 | 270 | std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; |
| ... | ... | @@ -366,7 +382,6 @@ TEST_F(TAppValue, OneString) { |
| 366 | 382 | |
| 367 | 383 | } |
| 368 | 384 | |
| 369 | -// TODO: Add vector arguments | |
| 370 | 385 | // TODO: Maybe add function to call on subcommand parse? Stashed. |
| 371 | 386 | // TODO: Check help output |
| 372 | 387 | // TODO: Add default/type info to help | ... | ... |