Commit 68e86a8085ecbbc39dba8d3b15e50cd622a92c36

Authored by Henry Fredrick Schreiner
1 parent 324a9c73

Adding better vector check

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&amp; output) { @@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T&amp; 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