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 63 #endif
64 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 77 struct Combiner {
67 78 int num;
68 79 bool positional;
... ... @@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T&amp; output) {
366 377  
367 378 // String and similar
368 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 394 bool lexical_cast(std::string input, T& output) {
371 395 logit("Direct lexical cast: " + input);
372 396 output = input;
... ... @@ -483,7 +507,7 @@ public:
483 507 }
484 508  
485 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 511 Option* add_option(
488 512 std::string name, ///< The name, long,short
489 513 T &variable, ///< The variable to set
... ... @@ -598,13 +622,16 @@ public:
598 622  
599 623 /// Prototype for new output style
600 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 626 Value<T> make_option(
603 627 std::string name, ///< The name, short,long
604 628 std::string discription="",
605 629 Combiner opts=VALIDATORS
606 630 ) {
607 631  
  632 + if(opts.num!=1)
  633 + throw IncorrectConstruction("Must have ARGS(1).");
  634 +
608 635 Value<T> out(name);
609 636 std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
610 637  
... ... @@ -623,8 +650,8 @@ public:
623 650 }
624 651  
625 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 655 Value<T> make_option(
629 656 std::string name, ///< The name, short,long
630 657 const T& default_value,
... ... @@ -632,6 +659,9 @@ public:
632 659 Combiner opts=VALIDATORS
633 660 ) {
634 661  
  662 + if(opts.num!=1)
  663 + throw IncorrectConstruction("Must have ARGS(1).");
  664 +
635 665 Value<T> out(name);
636 666 std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
637 667 ptr->reset(new T(default_value)); // resets the internal ptr
... ... @@ -650,9 +680,10 @@ public:
650 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 687 std::string name, ///< The name, short,long
657 688 std::string discription="",
658 689 Combiner opts=VALIDATORS
... ... @@ -661,11 +692,11 @@ public:
661 692 if(opts.num==0)
662 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 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 700 bool retval = true;
670 701 for(const auto &a : res)
671 702 for(const auto &b : a) {
... ... @@ -841,23 +872,17 @@ public:
841 872  
842 873  
843 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 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 881 num--;
855 882 std::string current = args.back();
856 883 logit("Adding: "+current);
857 884 args.pop_back();
858 885 op->add_result(vnum,current);
859   - if(args.size()==0)
860   - return;
861 886 }
862 887  
863 888 if(rest != "") {
... ... @@ -916,22 +941,14 @@ public:
916 941 }
917 942  
918 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 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 949 num--;
930   - std::string current = args.back();
  950 + op->add_result(vnum,args.back());
931 951 args.pop_back();
932   - op->add_result(vnum,current);
933   - if(args.size()==0)
934   - return;
935 952 }
936 953 return;
937 954 }
... ...
tests/CLITest.cpp
... ... @@ -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 441 // TODO: Maybe add function to call on subcommand parse? Stashed.
386 442 // TODO: Check help output
387 443 // TODO: Add default/type info to help
388 444 // TODO: Add set checking
389 445 // TODO: Try all of the options together
390   -// TODO: Add make_option alternative with type? Cancelled for now
... ...