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

add separate condition for index into vectors

remove restrictions on tuple size, and add some additional tests and modified documentation

fix some issues with the negative number check

add some test for indexed validation on tuple

allow specific validators for specific elements in a type with multiple values, or to just apply to the last given argument
This commit is contained in:
Philip Top 2019-08-10 06:24:47 -07:00 committed by Henry Schreiner
parent 06ab2d0fbd
commit 67c441b527
7 changed files with 259 additions and 162 deletions

View File

@ -111,7 +111,7 @@ After I wrote this, I also found the following libraries:
| [Clara][] | Simple library built for the excellent [Catch][] testing framework. Unique syntax, limited scope. |
| [Argh!][] | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free. |
| [CLI][] | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active. |
|[argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |
| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. |
See [Awesome C++][] for a less-biased list of parsers. You can also find other single file libraries at [Single file libs][].
@ -194,7 +194,7 @@ While all options internally are the same type, there are several ways to add an
app.add_option(option_name, help_str="") // 🆕
app.add_option(option_name,
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples(up to 5 elements) and tuple like structures such as std::array or std::pair.
variable_to_bind_to, // bool, int, float, vector, 🆕 enum, or string-like, or anything with a defined conversion from a string or that takes an int🚧, double🚧, or string in a constructor. Also allowed are tuples🚧,std::array🚧 or std::pair🚧.
help_string="")
app.add_option_function<type>(option_name,
@ -202,7 +202,7 @@ app.add_option_function<type>(option_name,
help_string="")
app.add_complex(... // Special case: support for complex numbers
//🚧There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value.
//🚧There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
app.add_option<typename T, typename XC>(option_name,
T &output, // output must be assignable or constructible from a value of type XC
help_string="")
@ -261,7 +261,7 @@ otherwise the output would default to a string. The add_option can be used with
Type such as optional<int>, optional<double>, and optional<string> are supported directly, other optional types can be added using the two parameter template. See [CLI11 Internals][] for information on how this could done and how you can add your own converters for additional types.
Vector types can also be used int the two parameter template overload
Vector types can also be used in the two parameter template overload
```
std::vector<double> v1;
app.add_option<std::vector<double>,int>("--vs",v1);

View File

@ -1859,8 +1859,14 @@ class App {
return detail::Classifier::SUBCOMMAND;
if(detail::split_long(current, dummy1, dummy2))
return detail::Classifier::LONG;
if(detail::split_short(current, dummy1, dummy2))
if(detail::split_short(current, dummy1, dummy2)) {
if(dummy1[0] >= '0' && dummy1[0] <= '9') {
if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
return detail::Classifier::NONE;
}
}
return detail::Classifier::SHORT;
}
if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
return detail::Classifier::WINDOWS;
if((current == "++") && !name_.empty() && parent_ != nullptr)
@ -2314,7 +2320,7 @@ class App {
(opt->get_items_expected() < 0 && opt->count() == 0lu)) {
if(validate_positionals_) {
std::string pos = positional;
pos = opt->_validate(pos);
pos = opt->_validate(pos, 0);
if(!pos.empty()) {
continue;
}
@ -2334,7 +2340,7 @@ class App {
(static_cast<int>(opt->count()) < opt->get_items_expected() || opt->get_items_expected() < 0)) {
if(validate_positionals_) {
std::string pos = positional;
pos = opt->_validate(pos);
pos = opt->_validate(pos, 0);
if(!pos.empty()) {
continue;
}

View File

@ -408,8 +408,17 @@ class Option : public OptionBase<Option> {
if((validator_name.empty()) && (!validators_.empty())) {
return &(validators_.front());
}
throw OptionNotFound(std::string("Validator ") + validator_name + " Not Found");
throw OptionNotFound(std::string{"Validator "} + validator_name + " Not Found");
}
/// Get a Validator by index NOTE: this may not be the order of definition
Validator *get_validator(int index) {
if(index >= 0 && index < static_cast<int>(validators_.size())) {
return &(validators_[index]);
}
throw OptionNotFound("Validator index is not valid");
}
/// Sets required options
Option *needs(Option *opt) {
auto tup = needs_.insert(opt);
@ -679,8 +688,17 @@ class Option : public OptionBase<Option> {
// Run the validators (can change the string)
if(!validators_.empty()) {
int index = 0;
// this is not available until multi_option_policy with type_size_>0 is enabled and functional
// if(type_size_ > 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) {
// index = type_size_ - static_cast<int>(results_.size());
//}
if(type_size_ < 0 && multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast) { // for vector operations
index = expected_ - static_cast<int>(results_.size());
}
for(std::string &result : results_) {
auto err_msg = _validate(result);
auto err_msg = _validate(result, index);
++index;
if(!err_msg.empty())
throw ValidationError(get_name(), err_msg);
}
@ -977,16 +995,19 @@ class Option : public OptionBase<Option> {
private:
// Run a result through the validators
std::string _validate(std::string &result) {
std::string _validate(std::string &result, int index) {
std::string err_msg;
for(const auto &vali : validators_) {
try {
err_msg = vali(result);
} catch(const ValidationError &err) {
err_msg = err.what();
auto v = vali.get_application_index();
if(v == -1 || v == index) {
try {
err_msg = vali(result);
} catch(const ValidationError &err) {
err_msg = err.what();
}
if(!err_msg.empty())
break;
}
if(!err_msg.empty())
break;
}
return err_msg;
}

View File

@ -137,12 +137,14 @@ struct pair_adaptor<
// check for constructibility from a specific type and copy assignable used in the parse detection
template <typename T, typename C> class is_direct_constructible {
template <typename TT, typename CC>
static auto test(int) -> decltype(TT{std::declval<CC>()}, std::is_move_assignable<TT>());
static auto test(int, std::true_type) -> decltype(TT{std::declval<CC>()}, std::is_move_assignable<TT>());
template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
template <typename, typename> static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T, C>(0))::value;
static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
};
#ifdef __GNUC__
#pragma GCC diagnostic pop
@ -363,28 +365,28 @@ template <typename T> struct uncommon_type {
/// Assignable from double or int
template <typename T>
struct classify_object<
T,
typename std::enable_if<uncommon_type<T>::value && is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value && type_count<T>::value == 1>::type> {
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{number_constructible};
};
/// Assignable from int
template <typename T>
struct classify_object<
T,
typename std::enable_if<uncommon_type<T>::value && !is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value && type_count<T>::value == 1>::type> {
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
!is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{integer_constructible};
};
/// Assignable from double
template <typename T>
struct classify_object<
T,
typename std::enable_if<uncommon_type<T>::value && is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value && type_count<T>::value == 1>::type> {
struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{double_constructible};
};
@ -392,9 +394,9 @@ struct classify_object<
template <typename T>
struct classify_object<
T,
typename std::enable_if<(is_tuple_like<T>::value && uncommon_type<T>::value &&
!is_direct_constructible<T, double>::value && !is_direct_constructible<T, int>::value) ||
type_count<T>::value >= 2>::type> {
typename std::enable_if<type_count<T>::value >= 2 || (is_tuple_like<T>::value && uncommon_type<T>::value &&
!is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value)>::type> {
static constexpr objCategory value{tuple_value};
};
@ -454,54 +456,39 @@ constexpr const char *type_name() {
return type_name<typename T::value_type>();
}
/// Print name for tuple types
/// Print name for single element tuple types
template <
typename T,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 1, detail::enabler> = detail::dummy>
std::string type_name() {
return type_name<typename std::tuple_element<0, T>::type>();
}
/// Print type name for 2 element tuples
template <
typename T,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 2, detail::enabler> = detail::dummy>
std::string type_name() {
return std::string("[") + type_name<typename std::tuple_element<0, T>::type>() + "," +
type_name<typename std::tuple_element<1, T>::type>() + "]";
/// Empty string if the index > tuple size
template <typename T, std::size_t I>
inline typename std::enable_if<I == type_count<T>::value, std::string>::type tuple_name() {
return std::string{};
}
/// Print type name for 3 element tuples
template <
typename T,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 3, detail::enabler> = detail::dummy>
std::string type_name() {
return std::string("[") + type_name<typename std::tuple_element<0, T>::type>() + "," +
type_name<typename std::tuple_element<1, T>::type>() + "," +
type_name<typename std::tuple_element<2, T>::type>() + "]";
/// Recursively generate the tuple type name
template <typename T, std::size_t I>
inline typename std::enable_if < I<type_count<T>::value, std::string>::type tuple_name() {
std::string str = std::string(type_name<typename std::tuple_element<I, T>::type>()) + ',' + tuple_name<T, I + 1>();
if(str.back() == ',')
str.pop_back();
return str;
}
/// Print type name for 4 element tuples
/// Print type name for tuples with 2 or more elements
template <
typename T,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 4, detail::enabler> = detail::dummy>
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value >= 2, detail::enabler> = detail::dummy>
std::string type_name() {
return std::string("[") + type_name<typename std::tuple_element<0, T>::type>() + "," +
type_name<typename std::tuple_element<1, T>::type>() + "," +
type_name<typename std::tuple_element<2, T>::type>() + "," +
type_name<typename std::tuple_element<3, T>::type>() + "]";
auto tname = std::string(1, '[') + tuple_name<T, 0>();
tname.push_back(']');
return tname;
}
/// Print type name for 5 element tuples
template <
typename T,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 5, detail::enabler> = detail::dummy>
std::string type_name() {
return std::string("[") + type_name<typename std::tuple_element<0, T>::type>() + "," +
type_name<typename std::tuple_element<1, T>::type>() + "," +
type_name<typename std::tuple_element<2, T>::type>() + "," +
type_name<typename std::tuple_element<3, T>::type>() + "," +
type_name<typename std::tuple_element<4, T>::type>() + "]";
}
// Lexical cast
/// Convert a flag into an integer value typically binary flags
@ -777,101 +764,34 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
return (!output.empty()) && retval;
}
/// Conversion for single element tuple and single element tuple conversion type
template <class T,
class XC,
enable_if_t<type_count<T>::value == 1 && is_tuple_like<T>::value && is_tuple_like<XC>::value,
detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_assign<typename std::tuple_element<0, T>::type, typename std::tuple_element<0, XC>::type>(
strings[0], std::get<0>(output));
/// function template for converting tuples if the static Index is greater than the tuple size
template <class T, class XC, std::size_t I>
inline typename std::enable_if<I >= type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &,
T &) {
return true;
}
/// Tuple conversion operation
template <class T, class XC, std::size_t I>
inline typename std::enable_if <
I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) {
bool retval = true;
if(strings.size() > I) {
retval &= lexical_assign<
typename std::tuple_element<I, T>::type,
typename std::conditional<is_tuple_like<XC>::value, typename std::tuple_element<I, XC>::type, XC>::type>(
strings[I], std::get<I>(output));
}
retval &= tuple_conversion<T, XC, I + 1>(strings, output);
return retval;
}
/// Conversion for single element tuple and single defined type
template <class T,
class XC,
enable_if_t<type_count<T>::value == 1 && is_tuple_like<T>::value && !is_tuple_like<XC>::value,
detail::enabler> = detail::dummy>
/// Conversion for tuples
template <class T, class XC, enable_if_t<is_tuple_like<T>::value, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_assign<typename std::tuple_element<0, T>::type, XC>(strings[0], std::get<0>(output));
return retval;
}
/// conversion for two element tuple
template <class T, class XC, enable_if_t<type_count<T>::value == 2, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_cast(strings[0], std::get<0>(output));
if(strings.size() > 1) {
retval &= lexical_cast(strings[1], std::get<1>(output));
}
return retval;
}
/// conversion for three element tuple
template <class T, class XC, enable_if_t<type_count<T>::value == 3, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_cast(strings[0], std::get<0>(output));
if(strings.size() > 1) {
retval &= lexical_cast(strings[1], std::get<1>(output));
}
if(strings.size() > 2) {
retval &= lexical_cast(strings[2], std::get<2>(output));
}
return retval;
}
/// conversion for four element tuple
template <class T, class XC, enable_if_t<type_count<T>::value == 4, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_cast(strings[0], std::get<0>(output));
if(strings.size() > 1) {
retval &= lexical_cast(strings[1], std::get<1>(output));
}
if(strings.size() > 2) {
retval &= lexical_cast(strings[2], std::get<2>(output));
}
if(strings.size() > 3) {
retval &= lexical_cast(strings[3], std::get<3>(output));
}
return retval;
}
/// conversion for five element tuple
template <class T, class XC, enable_if_t<type_count<T>::value == 5, detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
static_assert(type_count<T>::value == type_count<XC>::value,
"when using converting to tuples different cross conversion are not possible");
bool retval = lexical_cast(strings[0], std::get<0>(output));
if(strings.size() > 1) {
retval &= lexical_cast(strings[1], std::get<1>(output));
}
if(strings.size() > 2) {
retval &= lexical_cast(strings[2], std::get<2>(output));
}
if(strings.size() > 3) {
retval &= lexical_cast(strings[3], std::get<3>(output));
}
if(strings.size() > 4) {
retval &= lexical_cast(strings[4], std::get<4>(output));
}
return retval;
static_assert(
!is_tuple_like<XC>::value || type_count<T>::value == type_count<XC>::value,
"if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
return tuple_conversion<T, XC, 0>(strings, output);
}
/// Sum a vector of flag representations

View File

@ -43,6 +43,8 @@ class Validator {
std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
/// The name for search purposes of the Validator
std::string name_;
/// A validate will only apply to an indexed value (-1 is all elements)
int application_index_ = -1;
/// Enable for Validator to allow it to be disabled if need be
bool active_{true};
/// specify that a validator should not modify the input
@ -113,7 +115,13 @@ class Validator {
non_modifying_ = no_modify;
return *this;
}
/// Specify the application index of a validator
Validator &application_index(int app_index) {
application_index_ = app_index;
return *this;
};
/// Get the current value of the application index
int get_application_index() const { return application_index_; }
/// Get a boolean if the validator is active
bool get_active() const { return active_; }
@ -141,6 +149,7 @@ class Validator {
};
newval.active_ = (active_ & other.active_);
newval.application_index_ = application_index_;
return newval;
}
@ -164,6 +173,7 @@ class Validator {
return std::string("(") + s1 + ") OR (" + s2 + ")";
};
newval.active_ = (active_ & other.active_);
newval.application_index_ = application_index_;
return newval;
}
@ -186,6 +196,7 @@ class Validator {
return std::string{};
};
newval.active_ = active_;
newval.application_index_ = application_index_;
return newval;
}
@ -201,10 +212,10 @@ class Validator {
if((f1.empty()) || (f2.empty())) {
return f1 + f2;
}
return std::string("(") + f1 + ")" + merger + "(" + f2 + ")";
return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
};
}
};
}; // namespace CLI
/// Class wrapping some of the accessors of Validator
class CustomValidator : public Validator {

View File

@ -800,6 +800,19 @@ TEST_F(TApp, TakeLastOptMulti) {
EXPECT_EQ(vals, std::vector<int>({2, 3}));
}
TEST_F(TApp, TakeLastOptMultiCheck) {
std::vector<int> vals;
auto opt = app.add_option("--long", vals)->expected(2)->take_last();
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
opt->check((!CLI::PositiveNumber).application_index(1));
args = {"--long", "-1", "2", "-3"};
EXPECT_NO_THROW(run());
EXPECT_EQ(vals, std::vector<int>({2, -3}));
}
TEST_F(TApp, TakeFirstOptMulti) {
std::vector<int> vals;
app.add_option("--long", vals)->expected(2)->take_first();
@ -1591,6 +1604,63 @@ TEST_F(TApp, NotFileExists) {
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
}
TEST_F(TApp, pair_check) {
std::string myfile{"pair_check_file.txt"};
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
std::pair<std::string, int> findex;
auto v0 = CLI::ExistingFile;
v0.application_index(0);
auto v1 = CLI::PositiveNumber;
v1.application_index(1);
app.add_option("--file", findex)->check(v0)->check(v1);
args = {"--file", myfile, "2"};
EXPECT_NO_THROW(run());
EXPECT_EQ(findex.first, myfile);
EXPECT_EQ(findex.second, 2);
args = {"--file", myfile, "-3"};
EXPECT_THROW(run(), CLI::ValidationError);
args = {"--file", myfile, "2"};
std::remove(myfile.c_str());
EXPECT_THROW(run(), CLI::ValidationError);
}
// this will require that modifying the multi-option policy for tuples be allowed which it isn't at present
/*
TEST_F(TApp, pair_check_take_first) {
std::string myfile{"pair_check_file2.txt"};
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
std::pair<std::string, int> findex;
auto opt = app.add_option("--file", findex)->check(CLI::ExistingFile)->check(CLI::PositiveNumber);
EXPECT_THROW(opt->get_validator(3), CLI::OptionNotFound);
opt->get_validator(0)->application_index(0);
opt->get_validator(1)->application_index(1);
opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
args = {"--file", "not_a_file.txt", "-16", "--file", myfile, "2"};
// should only check the last one
EXPECT_NO_THROW(run());
EXPECT_EQ(findex.first, myfile);
EXPECT_EQ(findex.second, 2);
opt->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst);
EXPECT_THROW(run(), CLI::ValidationError);
}
*/
TEST_F(TApp, VectorFixedString) {
std::vector<std::string> strvec;
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
@ -1617,6 +1687,24 @@ TEST_F(TApp, VectorDefaultedFixedString) {
EXPECT_EQ(answer, strvec);
}
TEST_F(TApp, VectorIndexedValidator) {
std::vector<int> vvec;
CLI::Option *opt = app.add_option("-v", vvec);
args = {"-v", "1", "-1", "-v", "3", "-v", "-976"};
run();
EXPECT_EQ(4u, app.count("-v"));
EXPECT_EQ(4u, vvec.size());
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
opt->check((!CLI::PositiveNumber).application_index(1));
EXPECT_NO_THROW(run());
EXPECT_EQ(4u, vvec.size());
// v[3] would be negative
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(3));
EXPECT_THROW(run(), CLI::ValidationError);
}
TEST_F(TApp, DefaultedResult) {
std::string sval = "NA";
int ival;
@ -2118,6 +2206,20 @@ TEST_F(TApp, CustomDoubleOption) {
EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
}
// now with tuple support this is possible
TEST_F(TApp, CustomDoubleOptionAlt) {
std::pair<int, double> custom_opt;
app.add_option("posit", custom_opt);
args = {"12", "1.5"};
run();
EXPECT_EQ(custom_opt.first, 12);
EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
}
// #128
TEST_F(TApp, RepeatingMultiArgumentOptions) {
std::vector<std::string> entries;

View File

@ -836,6 +836,9 @@ TEST(Types, TypeName) {
tuple_name = CLI::detail::type_name<std::tuple<int, std::string, double, unsigned int, std::string>>();
EXPECT_EQ("[INT,TEXT,FLOAT,UINT,TEXT]", tuple_name);
tuple_name = CLI::detail::type_name<std::array<int, 10>>();
EXPECT_EQ("[INT,INT,INT,INT,INT,INT,INT,INT,INT,INT]", tuple_name);
std::string text_name = CLI::detail::type_name<std::string>();
EXPECT_EQ("TEXT", text_name);
@ -1005,7 +1008,8 @@ TEST(Types, LexicalConversionVectorDouble) {
static_assert(!CLI::detail::is_tuple_like<std::vector<double>>::value, "vector should not be like a tuple");
static_assert(CLI::detail::is_tuple_like<std::pair<double, double>>::value, "pair of double should be like a tuple");
static_assert(CLI::detail::is_tuple_like<std::array<double, 4>>::value, "std::array should be like a tuple");
static_assert(CLI::detail::is_tuple_like<std::array<double, 4>>::value, "std::array<double,4> should be like a tuple");
static_assert(CLI::detail::is_tuple_like<std::array<int, 10>>::value, "std::array<int,10> should be like a tuple");
static_assert(!CLI::detail::is_tuple_like<std::string>::value, "std::string should not be like a tuple");
static_assert(!CLI::detail::is_tuple_like<double>::value, "double should not be like a tuple");
static_assert(CLI::detail::is_tuple_like<std::tuple<double, int, double>>::value, "tuple should look like a tuple");
@ -1071,7 +1075,40 @@ TEST(Types, LexicalConversionTuple5) {
EXPECT_FALSE(res);
}
TEST(Types, LexicalConversionXomplwz) {
TEST(Types, LexicalConversionTuple10) {
CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
std::array<unsigned int, 10> x;
bool res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
EXPECT_TRUE(res);
EXPECT_EQ(std::get<1>(x), 19u);
EXPECT_EQ(x[0], 9u);
EXPECT_EQ(x[2], 18u);
EXPECT_EQ(x[3], 5u);
EXPECT_EQ(x[4], 235235u);
EXPECT_EQ(x[9], 235235u);
input[3] = "hippo";
res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
EXPECT_FALSE(res);
}
TEST(Types, LexicalConversionTuple10XC) {
CLI::results_t input = {"9", "19", "18", "5", "235235", "9", "19", "18", "5", "235235"};
std::array<double, 10> x;
bool res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
EXPECT_TRUE(res);
EXPECT_EQ(std::get<1>(x), 19.0);
EXPECT_EQ(x[0], 9.0);
EXPECT_EQ(x[2], 18.0);
EXPECT_EQ(x[3], 5.0);
EXPECT_EQ(x[4], 235235.0);
EXPECT_EQ(x[9], 235235.0);
input[3] = "19.7";
res = CLI::detail::lexical_conversion<decltype(x), std::array<unsigned int, 10>>(input, x);
EXPECT_FALSE(res);
}
TEST(Types, LexicalConversionComplex) {
CLI::results_t input = {"5.1", "3.5"};
std::complex<double> x;
bool res = CLI::detail::lexical_conversion<std::complex<double>, std::array<double, 2>>(input, x);