Commit 68e86a8085ecbbc39dba8d3b15e50cd622a92c36
1 parent
324a9c73
Adding better vector check
Showing
2 changed files
with
105 additions
and
33 deletions
include/CLI.hpp
| @@ -63,6 +63,17 @@ using std::enable_if_t; | @@ -63,6 +63,17 @@ using std::enable_if_t; | ||
| 63 | #endif | 63 | #endif |
| 64 | // If your compiler supports C++14, you can use that definition instead | 64 | // If your compiler supports C++14, you can use that definition instead |
| 65 | 65 | ||
| 66 | +template <typename T> | ||
| 67 | +struct is_vector { | ||
| 68 | + static const bool value = false; | ||
| 69 | +}; | ||
| 70 | + | ||
| 71 | + | ||
| 72 | +template<class T, class A> | ||
| 73 | +struct is_vector<std::vector<T, A> > { | ||
| 74 | + static bool const value = true; | ||
| 75 | +}; | ||
| 76 | + | ||
| 66 | struct Combiner { | 77 | struct Combiner { |
| 67 | int num; | 78 | int num; |
| 68 | bool positional; | 79 | bool positional; |
| @@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T& output) { | @@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T& output) { | ||
| 366 | 377 | ||
| 367 | // String and similar | 378 | // String and similar |
| 368 | template<typename T, | 379 | template<typename T, |
| 369 | -enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value, detail::enabler> = dummy> | 380 | +enable_if_t<is_vector<T>::value, detail::enabler> = dummy> |
| 381 | +bool lexical_cast(std::string input, T& output) { | ||
| 382 | + logit("vector lexical cast: " + input); | ||
| 383 | + if(output.size() == input.size()) | ||
| 384 | + output.resize(input.size()); | ||
| 385 | + for(size_t i=0; i<input.size(); i++) | ||
| 386 | + output[i] = input[i]; | ||
| 387 | + return true; | ||
| 388 | +} | ||
| 389 | + | ||
| 390 | +// String and similar | ||
| 391 | +template<typename T, | ||
| 392 | +enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value | ||
| 393 | +, detail::enabler> = dummy> | ||
| 370 | bool lexical_cast(std::string input, T& output) { | 394 | bool lexical_cast(std::string input, T& output) { |
| 371 | logit("Direct lexical cast: " + input); | 395 | logit("Direct lexical cast: " + input); |
| 372 | output = input; | 396 | output = input; |
| @@ -483,7 +507,7 @@ public: | @@ -483,7 +507,7 @@ public: | ||
| 483 | } | 507 | } |
| 484 | 508 | ||
| 485 | /// Add option for string | 509 | /// Add option for string |
| 486 | - template<typename T, enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> | 510 | + template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> |
| 487 | Option* add_option( | 511 | Option* add_option( |
| 488 | std::string name, ///< The name, long,short | 512 | std::string name, ///< The name, long,short |
| 489 | T &variable, ///< The variable to set | 513 | T &variable, ///< The variable to set |
| @@ -598,13 +622,16 @@ public: | @@ -598,13 +622,16 @@ public: | ||
| 598 | 622 | ||
| 599 | /// Prototype for new output style | 623 | /// Prototype for new output style |
| 600 | template<typename T = std::string, | 624 | template<typename T = std::string, |
| 601 | - enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> | 625 | + enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> |
| 602 | Value<T> make_option( | 626 | Value<T> make_option( |
| 603 | std::string name, ///< The name, short,long | 627 | std::string name, ///< The name, short,long |
| 604 | std::string discription="", | 628 | std::string discription="", |
| 605 | Combiner opts=VALIDATORS | 629 | Combiner opts=VALIDATORS |
| 606 | ) { | 630 | ) { |
| 607 | 631 | ||
| 632 | + if(opts.num!=1) | ||
| 633 | + throw IncorrectConstruction("Must have ARGS(1)."); | ||
| 634 | + | ||
| 608 | Value<T> out(name); | 635 | Value<T> out(name); |
| 609 | std::shared_ptr<std::unique_ptr<T>> ptr = out.value; | 636 | std::shared_ptr<std::unique_ptr<T>> ptr = out.value; |
| 610 | 637 | ||
| @@ -623,8 +650,8 @@ public: | @@ -623,8 +650,8 @@ public: | ||
| 623 | } | 650 | } |
| 624 | 651 | ||
| 625 | /// Prototype for new output style with default | 652 | /// 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> | 653 | + template<typename T, |
| 654 | + enable_if_t<!is_vector<T>::value, detail::enabler> = dummy> | ||
| 628 | Value<T> make_option( | 655 | Value<T> make_option( |
| 629 | std::string name, ///< The name, short,long | 656 | std::string name, ///< The name, short,long |
| 630 | const T& default_value, | 657 | const T& default_value, |
| @@ -632,6 +659,9 @@ public: | @@ -632,6 +659,9 @@ public: | ||
| 632 | Combiner opts=VALIDATORS | 659 | Combiner opts=VALIDATORS |
| 633 | ) { | 660 | ) { |
| 634 | 661 | ||
| 662 | + if(opts.num!=1) | ||
| 663 | + throw IncorrectConstruction("Must have ARGS(1)."); | ||
| 664 | + | ||
| 635 | Value<T> out(name); | 665 | Value<T> out(name); |
| 636 | std::shared_ptr<std::unique_ptr<T>> ptr = out.value; | 666 | std::shared_ptr<std::unique_ptr<T>> ptr = out.value; |
| 637 | ptr->reset(new T(default_value)); // resets the internal ptr | 667 | ptr->reset(new T(default_value)); // resets the internal ptr |
| @@ -650,9 +680,10 @@ public: | @@ -650,9 +680,10 @@ public: | ||
| 650 | return out; | 680 | return out; |
| 651 | } | 681 | } |
| 652 | 682 | ||
| 653 | - /// Prototype for new output style | ||
| 654 | - template<typename T> | ||
| 655 | - Value<std::vector<T>> make_option( | 683 | + /// Prototype for new output style, vector |
| 684 | + template<typename T, | ||
| 685 | + enable_if_t<is_vector<T>::value, detail::enabler> = dummy> | ||
| 686 | + Value<T> make_option( | ||
| 656 | std::string name, ///< The name, short,long | 687 | std::string name, ///< The name, short,long |
| 657 | std::string discription="", | 688 | std::string discription="", |
| 658 | Combiner opts=VALIDATORS | 689 | Combiner opts=VALIDATORS |
| @@ -661,11 +692,11 @@ public: | @@ -661,11 +692,11 @@ public: | ||
| 661 | if(opts.num==0) | 692 | if(opts.num==0) |
| 662 | throw IncorrectConstruction("Must have ARGS or be a vector."); | 693 | throw IncorrectConstruction("Must have ARGS or be a vector."); |
| 663 | 694 | ||
| 664 | - Value<std::vector<T>> out(name); | ||
| 665 | - std::shared_ptr<std::unique_ptr<std::vector<T> >> ptr = out.value; | 695 | + Value<T> out(name); |
| 696 | + std::shared_ptr<std::unique_ptr<T>> ptr = out.value; | ||
| 666 | 697 | ||
| 667 | CLI::callback_t fun = [ptr](CLI::results_t res){ | 698 | CLI::callback_t fun = [ptr](CLI::results_t res){ |
| 668 | - ptr->reset(new std::vector<T>()); // resets the internal ptr | 699 | + ptr->reset(new T()); // resets the internal ptr |
| 669 | bool retval = true; | 700 | bool retval = true; |
| 670 | for(const auto &a : res) | 701 | for(const auto &a : res) |
| 671 | for(const auto &b : a) { | 702 | for(const auto &b : a) { |
| @@ -841,23 +872,17 @@ public: | @@ -841,23 +872,17 @@ public: | ||
| 841 | 872 | ||
| 842 | 873 | ||
| 843 | if(num == -1) { | 874 | if(num == -1) { |
| 844 | - std::string current = args.back(); | ||
| 845 | - while(_recognize(current) == Classifer::NONE) { | ||
| 846 | - std::string current = args.back(); | 875 | + while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) { |
| 876 | + op->add_result(vnum, args.back()); | ||
| 847 | args.pop_back(); | 877 | args.pop_back(); |
| 848 | - op->add_result(vnum,current); | ||
| 849 | - if(args.size()==0) | ||
| 850 | - return; | ||
| 851 | 878 | ||
| 852 | } | 879 | } |
| 853 | - } else while(num>0) { | 880 | + } else while(num>0 && args.size() > 0) { |
| 854 | num--; | 881 | num--; |
| 855 | std::string current = args.back(); | 882 | std::string current = args.back(); |
| 856 | logit("Adding: "+current); | 883 | logit("Adding: "+current); |
| 857 | args.pop_back(); | 884 | args.pop_back(); |
| 858 | op->add_result(vnum,current); | 885 | op->add_result(vnum,current); |
| 859 | - if(args.size()==0) | ||
| 860 | - return; | ||
| 861 | } | 886 | } |
| 862 | 887 | ||
| 863 | if(rest != "") { | 888 | if(rest != "") { |
| @@ -916,22 +941,14 @@ public: | @@ -916,22 +941,14 @@ public: | ||
| 916 | } | 941 | } |
| 917 | 942 | ||
| 918 | if(num == -1) { | 943 | if(num == -1) { |
| 919 | - std::string current = args.back(); | ||
| 920 | - while(_recognize(current) == Classifer::NONE) { | ||
| 921 | - std::string current = args.back(); | 944 | + while(args.size() > 0 && _recognize(args.back()) == Classifer::NONE) { |
| 945 | + op->add_result(vnum, args.back()); | ||
| 922 | args.pop_back(); | 946 | args.pop_back(); |
| 923 | - op->add_result(vnum,current); | ||
| 924 | - if(args.size()==0) | ||
| 925 | - return; | ||
| 926 | - | ||
| 927 | } | 947 | } |
| 928 | - } else while(num>0) { | 948 | + } else while(num>0 && args.size()>0) { |
| 929 | num--; | 949 | num--; |
| 930 | - std::string current = args.back(); | 950 | + op->add_result(vnum,args.back()); |
| 931 | args.pop_back(); | 951 | args.pop_back(); |
| 932 | - op->add_result(vnum,current); | ||
| 933 | - if(args.size()==0) | ||
| 934 | - return; | ||
| 935 | } | 952 | } |
| 936 | return; | 953 | return; |
| 937 | } | 954 | } |
tests/CLITest.cpp
| @@ -382,9 +382,64 @@ TEST_F(TAppValue, OneString) { | @@ -382,9 +382,64 @@ TEST_F(TAppValue, OneString) { | ||
| 382 | 382 | ||
| 383 | } | 383 | } |
| 384 | 384 | ||
| 385 | +TEST_F(TAppValue, SeveralInts) { | ||
| 386 | + auto value = app.make_option<int>("first"); | ||
| 387 | + CLI::Value<int> value2 = app.make_option<int>("s"); | ||
| 388 | + int v; | ||
| 389 | + args = {"--first", "12", "-s", "19"}; | ||
| 390 | + EXPECT_FALSE((bool) value); | ||
| 391 | + EXPECT_FALSE((bool) value2); | ||
| 392 | + | ||
| 393 | + EXPECT_THROW(v = *value, CLI::EmptyError); | ||
| 394 | + //EXPECT_THROW(v = str, CLI::EmptyError); | ||
| 395 | + EXPECT_NO_THROW(run()); | ||
| 396 | + EXPECT_TRUE((bool) value); | ||
| 397 | + EXPECT_NO_THROW(v = *value); | ||
| 398 | + EXPECT_NO_THROW(v = value); | ||
| 399 | + | ||
| 400 | + EXPECT_EQ(1, app.count("s")); | ||
| 401 | + EXPECT_EQ(1, app.count("first")); | ||
| 402 | + EXPECT_EQ(*value, 12); | ||
| 403 | + EXPECT_EQ(*value2, 19); | ||
| 404 | + | ||
| 405 | +} | ||
| 406 | + | ||
| 407 | +TEST_F(TAppValue, Vector) { | ||
| 408 | + auto value = app.make_option<std::vector<int>>("first", "", CLI::ARGS); | ||
| 409 | + auto value2 = app.make_option<std::vector<std::string>>("second", "", CLI::ARGS); | ||
| 410 | + | ||
| 411 | + std::vector<int> i; | ||
| 412 | + std::vector<std::string> s; | ||
| 413 | + | ||
| 414 | + args = {"--first", "12", "3", "9", "--second", "thing", "try"}; | ||
| 415 | + | ||
| 416 | + EXPECT_FALSE((bool) value); | ||
| 417 | + EXPECT_FALSE((bool) value2); | ||
| 418 | + | ||
| 419 | + EXPECT_THROW(i = *value, CLI::EmptyError); | ||
| 420 | + EXPECT_THROW(s = *value2, CLI::EmptyError); | ||
| 421 | + | ||
| 422 | + EXPECT_NO_THROW(run()); | ||
| 423 | + | ||
| 424 | + EXPECT_TRUE((bool) value); | ||
| 425 | + EXPECT_TRUE((bool) value2); | ||
| 426 | + | ||
| 427 | + EXPECT_NO_THROW(i = *value); | ||
| 428 | + //EXPECT_NO_THROW(i = value); | ||
| 429 | + | ||
| 430 | + EXPECT_NO_THROW(s = *value2); | ||
| 431 | + //EXPECT_NO_THROW(s = value2); | ||
| 432 | + | ||
| 433 | + EXPECT_EQ(3, app.count("first")); | ||
| 434 | + EXPECT_EQ(2, app.count("second")); | ||
| 435 | + | ||
| 436 | + EXPECT_EQ(*value, std::vector<int>({12,3,9})); | ||
| 437 | + EXPECT_EQ(*value2, std::vector<std::string>({"thing", "try"})); | ||
| 438 | + | ||
| 439 | +} | ||
| 440 | + | ||
| 385 | // TODO: Maybe add function to call on subcommand parse? Stashed. | 441 | // TODO: Maybe add function to call on subcommand parse? Stashed. |
| 386 | // TODO: Check help output | 442 | // TODO: Check help output |
| 387 | // TODO: Add default/type info to help | 443 | // TODO: Add default/type info to help |
| 388 | // TODO: Add set checking | 444 | // TODO: Add set checking |
| 389 | // TODO: Try all of the options together | 445 | // TODO: Try all of the options together |
| 390 | -// TODO: Add make_option alternative with type? Cancelled for now |