1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 12:43:52 +00:00

Adding better vector check

This commit is contained in:
Henry Fredrick Schreiner 2017-01-31 11:31:57 -05:00
parent 324a9c738d
commit 68e86a8085
2 changed files with 105 additions and 33 deletions

View File

@ -63,6 +63,17 @@ using std::enable_if_t;
#endif #endif
// If your compiler supports C++14, you can use that definition instead // If your compiler supports C++14, you can use that definition instead
template <typename T>
struct is_vector {
static const bool value = false;
};
template<class T, class A>
struct is_vector<std::vector<T, A> > {
static bool const value = true;
};
struct Combiner { struct Combiner {
int num; int num;
bool positional; bool positional;
@ -366,7 +377,20 @@ bool lexical_cast(std::string input, T& output) {
// String and similar // String and similar
template<typename T, template<typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value, detail::enabler> = dummy> enable_if_t<is_vector<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<input.size(); i++)
output[i] = input[i];
return true;
}
// String and similar
template<typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value
, detail::enabler> = dummy>
bool lexical_cast(std::string input, T& output) { bool lexical_cast(std::string input, T& output) {
logit("Direct lexical cast: " + input); logit("Direct lexical cast: " + input);
output = input; output = input;
@ -483,7 +507,7 @@ public:
} }
/// Add option for string /// Add option for string
template<typename T, enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = dummy>
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name, ///< The name, long,short
T &variable, ///< The variable to set T &variable, ///< The variable to set
@ -598,13 +622,16 @@ public:
/// Prototype for new output style /// Prototype for new output style
template<typename T = std::string, template<typename T = std::string,
enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
std::string discription="", std::string discription="",
Combiner opts=VALIDATORS Combiner opts=VALIDATORS
) { ) {
if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1).");
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -623,8 +650,8 @@ public:
} }
/// Prototype for new output style with default /// Prototype for new output style with default
template<typename T = std::string, template<typename T,
enable_if_t<!std::is_array<T>::value, detail::enabler> = dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
const T& default_value, const T& default_value,
@ -632,6 +659,9 @@ public:
Combiner opts=VALIDATORS Combiner opts=VALIDATORS
) { ) {
if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1).");
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
ptr->reset(new T(default_value)); // resets the internal ptr ptr->reset(new T(default_value)); // resets the internal ptr
@ -650,9 +680,10 @@ public:
return out; return out;
} }
/// Prototype for new output style /// Prototype for new output style, vector
template<typename T> template<typename T,
Value<std::vector<T>> make_option( enable_if_t<is_vector<T>::value, detail::enabler> = dummy>
Value<T> make_option(
std::string name, ///< The name, short,long std::string name, ///< The name, short,long
std::string discription="", std::string discription="",
Combiner opts=VALIDATORS Combiner opts=VALIDATORS
@ -661,11 +692,11 @@ public:
if(opts.num==0) if(opts.num==0)
throw IncorrectConstruction("Must have ARGS or be a vector."); throw IncorrectConstruction("Must have ARGS or be a vector.");
Value<std::vector<T>> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<std::vector<T> >> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
CLI::callback_t fun = [ptr](CLI::results_t res){ CLI::callback_t fun = [ptr](CLI::results_t res){
ptr->reset(new std::vector<T>()); // resets the internal ptr ptr->reset(new T()); // resets the internal ptr
bool retval = true; bool retval = true;
for(const auto &a : res) for(const auto &a : res)
for(const auto &b : a) { for(const auto &b : a) {
@ -841,23 +872,17 @@ public:
if(num == -1) { if(num == -1) {
std::string current = args.back(); while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) {
while(_recognize(current) == Classifer::NONE) { op->add_result(vnum, args.back());
std::string current = args.back();
args.pop_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--; num--;
std::string current = args.back(); std::string current = args.back();
logit("Adding: "+current); logit("Adding: "+current);
args.pop_back(); args.pop_back();
op->add_result(vnum,current); op->add_result(vnum,current);
if(args.size()==0)
return;
} }
if(rest != "") { if(rest != "") {
@ -916,22 +941,14 @@ public:
} }
if(num == -1) { if(num == -1) {
std::string current = args.back(); while(args.size() > 0 && _recognize(args.back()) == Classifer::NONE) {
while(_recognize(current) == Classifer::NONE) { op->add_result(vnum, args.back());
std::string current = args.back();
args.pop_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--; num--;
std::string current = args.back(); op->add_result(vnum,args.back());
args.pop_back(); args.pop_back();
op->add_result(vnum,current);
if(args.size()==0)
return;
} }
return; return;
} }

View File

@ -382,9 +382,64 @@ TEST_F(TAppValue, OneString) {
} }
TEST_F(TAppValue, SeveralInts) {
auto value = app.make_option<int>("first");
CLI::Value<int> value2 = app.make_option<int>("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<std::vector<int>>("first", "", CLI::ARGS);
auto value2 = app.make_option<std::vector<std::string>>("second", "", CLI::ARGS);
std::vector<int> i;
std::vector<std::string> 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<int>({12,3,9}));
EXPECT_EQ(*value2, std::vector<std::string>({"thing", "try"}));
}
// TODO: Maybe add function to call on subcommand parse? Stashed. // TODO: Maybe add function to call on subcommand parse? Stashed.
// TODO: Check help output // TODO: Check help output
// TODO: Add default/type info to help // TODO: Add default/type info to help
// TODO: Add set checking // TODO: Add set checking
// TODO: Try all of the options together // TODO: Try all of the options together
// TODO: Add make_option alternative with type? Cancelled for now