diff --git a/include/CLI.hpp b/include/CLI.hpp index 8469e9f9..88970a89 100644 --- a/include/CLI.hpp +++ b/include/CLI.hpp @@ -63,6 +63,17 @@ using std::enable_if_t; #endif // If your compiler supports C++14, you can use that definition instead +template +struct is_vector { + static const bool value = false; +}; + + +template +struct is_vector > { + static bool const value = true; +}; + struct Combiner { int num; bool positional; @@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T& output) { // String and similar template::value && !std::is_integral::value, detail::enabler> = dummy> +enable_if_t::value, detail::enabler> = dummy> +bool lexical_cast(std::string input, T& output) { + logit("vector lexical cast: " + input); + if(output.size() == input.size()) + output.resize(input.size()); + for(size_t i=0; i::value && !std::is_integral::value && !is_vector::value +, detail::enabler> = dummy> bool lexical_cast(std::string input, T& output) { logit("Direct lexical cast: " + input); output = input; @@ -483,7 +507,7 @@ public: } /// Add option for string - template::value, detail::enabler> = dummy> + template::value, detail::enabler> = dummy> Option* add_option( std::string name, ///< The name, long,short T &variable, ///< The variable to set @@ -598,13 +622,16 @@ public: /// Prototype for new output style template::value, detail::enabler> = dummy> + enable_if_t::value, detail::enabler> = dummy> Value make_option( std::string name, ///< The name, short,long std::string discription="", Combiner opts=VALIDATORS ) { + if(opts.num!=1) + throw IncorrectConstruction("Must have ARGS(1)."); + Value out(name); std::shared_ptr> ptr = out.value; @@ -623,8 +650,8 @@ public: } /// Prototype for new output style with default - template::value, detail::enabler> = dummy> + template::value, detail::enabler> = dummy> Value make_option( std::string name, ///< The name, short,long const T& default_value, @@ -632,6 +659,9 @@ public: Combiner opts=VALIDATORS ) { + if(opts.num!=1) + throw IncorrectConstruction("Must have ARGS(1)."); + Value out(name); std::shared_ptr> ptr = out.value; ptr->reset(new T(default_value)); // resets the internal ptr @@ -650,9 +680,10 @@ public: return out; } - /// Prototype for new output style - template - Value> make_option( + /// Prototype for new output style, vector + template::value, detail::enabler> = dummy> + Value make_option( std::string name, ///< The name, short,long std::string discription="", Combiner opts=VALIDATORS @@ -661,11 +692,11 @@ public: if(opts.num==0) throw IncorrectConstruction("Must have ARGS or be a vector."); - Value> out(name); - std::shared_ptr >> ptr = out.value; + Value out(name); + std::shared_ptr> ptr = out.value; CLI::callback_t fun = [ptr](CLI::results_t res){ - ptr->reset(new std::vector()); // resets the internal ptr + ptr->reset(new T()); // resets the internal ptr bool retval = true; for(const auto &a : res) for(const auto &b : a) { @@ -841,23 +872,17 @@ public: if(num == -1) { - std::string current = args.back(); - while(_recognize(current) == Classifer::NONE) { - std::string current = args.back(); + while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) { + op->add_result(vnum, args.back()); args.pop_back(); - op->add_result(vnum,current); - if(args.size()==0) - return; } - } else while(num>0) { + } else while(num>0 && args.size() > 0) { num--; std::string current = args.back(); logit("Adding: "+current); args.pop_back(); op->add_result(vnum,current); - if(args.size()==0) - return; } if(rest != "") { @@ -916,22 +941,14 @@ public: } if(num == -1) { - std::string current = args.back(); - while(_recognize(current) == Classifer::NONE) { - std::string current = args.back(); + while(args.size() > 0 && _recognize(args.back()) == Classifer::NONE) { + op->add_result(vnum, args.back()); args.pop_back(); - op->add_result(vnum,current); - if(args.size()==0) - return; - } - } else while(num>0) { + } else while(num>0 && args.size()>0) { num--; - std::string current = args.back(); + op->add_result(vnum,args.back()); args.pop_back(); - op->add_result(vnum,current); - if(args.size()==0) - return; } return; } diff --git a/tests/CLITest.cpp b/tests/CLITest.cpp index 308b8087..4af13f1a 100644 --- a/tests/CLITest.cpp +++ b/tests/CLITest.cpp @@ -382,9 +382,64 @@ TEST_F(TAppValue, OneString) { } +TEST_F(TAppValue, SeveralInts) { + auto value = app.make_option("first"); + CLI::Value value2 = app.make_option("s"); + int v; + args = {"--first", "12", "-s", "19"}; + EXPECT_FALSE((bool) value); + EXPECT_FALSE((bool) value2); + + EXPECT_THROW(v = *value, CLI::EmptyError); + //EXPECT_THROW(v = str, CLI::EmptyError); + EXPECT_NO_THROW(run()); + EXPECT_TRUE((bool) value); + EXPECT_NO_THROW(v = *value); + EXPECT_NO_THROW(v = value); + + EXPECT_EQ(1, app.count("s")); + EXPECT_EQ(1, app.count("first")); + EXPECT_EQ(*value, 12); + EXPECT_EQ(*value2, 19); + +} + +TEST_F(TAppValue, Vector) { + auto value = app.make_option>("first", "", CLI::ARGS); + auto value2 = app.make_option>("second", "", CLI::ARGS); + + std::vector i; + std::vector s; + + args = {"--first", "12", "3", "9", "--second", "thing", "try"}; + + EXPECT_FALSE((bool) value); + EXPECT_FALSE((bool) value2); + + EXPECT_THROW(i = *value, CLI::EmptyError); + EXPECT_THROW(s = *value2, CLI::EmptyError); + + EXPECT_NO_THROW(run()); + + EXPECT_TRUE((bool) value); + EXPECT_TRUE((bool) value2); + + EXPECT_NO_THROW(i = *value); + //EXPECT_NO_THROW(i = value); + + EXPECT_NO_THROW(s = *value2); + //EXPECT_NO_THROW(s = value2); + + EXPECT_EQ(3, app.count("first")); + EXPECT_EQ(2, app.count("second")); + + EXPECT_EQ(*value, std::vector({12,3,9})); + EXPECT_EQ(*value2, std::vector({"thing", "try"})); + +} + // TODO: Maybe add function to call on subcommand parse? Stashed. // TODO: Check help output // TODO: Add default/type info to help // TODO: Add set checking // TODO: Try all of the options together -// TODO: Add make_option alternative with type? Cancelled for now