mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-05-03 05:53:52 +00:00
feat: add char type (#449)
add a test for char options add support for char types to the lexical cast, to allow single character types that make sense, add a integral_conversion operations to simplify the conversions from string to integers and allow discrimination in a few cases with enumerations.
This commit is contained in:
parent
a1dd4d708d
commit
438eabe5f8
@ -224,7 +224,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, help_str="")
|
||||||
|
|
||||||
app.add_option(option_name,
|
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 🆕, std::array 🆕 or std::pair 🆕. Also supported are complex numbers🚧, wrapper types🚧, and containers besides vector🚧 of any other supported type.
|
variable_to_bind_to, // bool, char(see note)🚧, 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 🆕. Also supported are complex numbers🚧, wrapper types🚧, and containers besides vector🚧 of any other supported type.
|
||||||
help_string="")
|
help_string="")
|
||||||
|
|
||||||
app.add_option_function<type>(option_name,
|
app.add_option_function<type>(option_name,
|
||||||
@ -233,6 +233,8 @@ app.add_option_function<type>(option_name,
|
|||||||
|
|
||||||
app.add_complex(... // Special case: support for complex numbers ⚠️. Complex numbers are now fully supported in the add_option so this function is redundant.
|
app.add_complex(... // Special case: support for complex numbers ⚠️. Complex numbers are now fully supported in the add_option so this function is redundant.
|
||||||
|
|
||||||
|
// char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values.
|
||||||
|
|
||||||
// 🆕 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
|
// 🆕 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,
|
app.add_option<typename T, typename XC>(option_name,
|
||||||
T &output, // output must be assignable or constructible from a value of type XC
|
T &output, // output must be assignable or constructible from a value of type XC
|
||||||
|
@ -22,6 +22,7 @@ You can use any C++ int-like type, not just `int`. CLI11 understands the followi
|
|||||||
|-------------|-------|
|
|-------------|-------|
|
||||||
| number like | Integers, floats, bools, or any type that can be constructed from an integer or floating point number |
|
| number like | Integers, floats, bools, or any type that can be constructed from an integer or floating point number |
|
||||||
| string-like | std\::string, or anything that can be constructed from or assigned a std\::string |
|
| string-like | std\::string, or anything that can be constructed from or assigned a std\::string |
|
||||||
|
| char | For a single char, single string values are accepted, otherwise longer strings are treated as integral values and a conversion is attempted |
|
||||||
| complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" |
|
| complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" |
|
||||||
| enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) |
|
| enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) |
|
||||||
| container-like | a container(like vector) of any available types including other containers |
|
| container-like | a container(like vector) of any available types including other containers |
|
||||||
|
@ -505,6 +505,7 @@ struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value
|
|||||||
|
|
||||||
// Enumeration of the different supported categorizations of objects
|
// Enumeration of the different supported categorizations of objects
|
||||||
enum class object_category : int {
|
enum class object_category : int {
|
||||||
|
char_value = 1,
|
||||||
integral_value = 2,
|
integral_value = 2,
|
||||||
unsigned_integral = 4,
|
unsigned_integral = 4,
|
||||||
enumeration = 6,
|
enumeration = 6,
|
||||||
@ -525,27 +526,36 @@ enum class object_category : int {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Set of overloads to classify an object according to type
|
||||||
|
|
||||||
/// some type that is not otherwise recognized
|
/// some type that is not otherwise recognized
|
||||||
template <typename T, typename Enable = void> struct classify_object {
|
template <typename T, typename Enable = void> struct classify_object {
|
||||||
static constexpr object_category value{object_category::other};
|
static constexpr object_category value{object_category::other};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Set of overloads to classify an object according to type
|
/// Signed integers
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct classify_object<T,
|
struct classify_object<
|
||||||
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
|
T,
|
||||||
!is_bool<T>::value && !std::is_enum<T>::value>::type> {
|
typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && std::is_signed<T>::value &&
|
||||||
|
!is_bool<T>::value && !std::is_enum<T>::value>::type> {
|
||||||
static constexpr object_category value{object_category::integral_value};
|
static constexpr object_category value{object_category::integral_value};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Unsigned integers
|
/// Unsigned integers
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct classify_object<
|
struct classify_object<T,
|
||||||
T,
|
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value &&
|
||||||
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> {
|
!std::is_same<T, char>::value && !is_bool<T>::value>::type> {
|
||||||
static constexpr object_category value{object_category::unsigned_integral};
|
static constexpr object_category value{object_category::unsigned_integral};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// single character values
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<T, typename std::enable_if<std::is_same<T, char>::value && !std::is_enum<T>::value>::type> {
|
||||||
|
static constexpr object_category value{object_category::char_value};
|
||||||
|
};
|
||||||
|
|
||||||
/// Boolean values
|
/// Boolean values
|
||||||
template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
|
template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
|
||||||
static constexpr object_category value{object_category::boolean_value};
|
static constexpr object_category value{object_category::boolean_value};
|
||||||
@ -657,6 +667,12 @@ template <typename T> struct classify_object<T, typename std::enable_if<is_mutab
|
|||||||
/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
|
/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
|
||||||
/// But this is cleaner and works better in this case
|
/// But this is cleaner and works better in this case
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
|
||||||
|
constexpr const char *type_name() {
|
||||||
|
return "CHAR";
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<classify_object<T>::value == object_category::integral_value ||
|
enable_if_t<classify_object<T>::value == object_category::integral_value ||
|
||||||
classify_object<T>::value == object_category::integer_constructible,
|
classify_object<T>::value == object_category::integer_constructible,
|
||||||
@ -767,6 +783,30 @@ inline std::string type_name() {
|
|||||||
|
|
||||||
// Lexical cast
|
// Lexical cast
|
||||||
|
|
||||||
|
/// Convert to an unsigned integral
|
||||||
|
template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
|
||||||
|
bool integral_conversion(const std::string &input, T &output) noexcept {
|
||||||
|
if(input.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *val = nullptr;
|
||||||
|
std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
|
||||||
|
output = static_cast<T>(output_ll);
|
||||||
|
return val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to a signed integral
|
||||||
|
template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
|
||||||
|
bool integral_conversion(const std::string &input, T &output) noexcept {
|
||||||
|
if(input.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *val = nullptr;
|
||||||
|
std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
|
||||||
|
output = static_cast<T>(output_ll);
|
||||||
|
return val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll;
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert a flag into an integer value typically binary flags
|
/// Convert a flag into an integer value typically binary flags
|
||||||
inline std::int64_t to_flag_value(std::string val) {
|
inline std::int64_t to_flag_value(std::string val) {
|
||||||
static const std::string trueString("true");
|
static const std::string trueString("true");
|
||||||
@ -810,39 +850,24 @@ inline std::int64_t to_flag_value(std::string val) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Signed integers
|
/// Integer conversion
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<classify_object<T>::value == object_category::integral_value, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::integral_value ||
|
||||||
|
classify_object<T>::value == object_category::unsigned_integral,
|
||||||
|
detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
try {
|
return integral_conversion(input, output);
|
||||||
std::size_t n = 0;
|
|
||||||
std::int64_t output_ll = std::stoll(input, &n, 0);
|
|
||||||
output = static_cast<T>(output_ll);
|
|
||||||
return n == input.size() && static_cast<std::int64_t>(output) == output_ll;
|
|
||||||
} catch(const std::invalid_argument &) {
|
|
||||||
return false;
|
|
||||||
} catch(const std::out_of_range &) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unsigned integers
|
/// char values
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
if(!input.empty() && input.front() == '-')
|
if(input.size() == 1) {
|
||||||
return false; // std::stoull happily converts negative values to junk without any errors.
|
output = static_cast<T>(input[0]);
|
||||||
|
return true;
|
||||||
try {
|
|
||||||
std::size_t n = 0;
|
|
||||||
std::uint64_t output_ll = std::stoull(input, &n, 0);
|
|
||||||
output = static_cast<T>(output_ll);
|
|
||||||
return n == input.size() && static_cast<std::uint64_t>(output) == output_ll;
|
|
||||||
} catch(const std::invalid_argument &) {
|
|
||||||
return false;
|
|
||||||
} catch(const std::out_of_range &) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return integral_conversion(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boolean values
|
/// Boolean values
|
||||||
@ -867,15 +892,13 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
try {
|
if(input.empty()) {
|
||||||
std::size_t n = 0;
|
|
||||||
output = static_cast<T>(std::stold(input, &n));
|
|
||||||
return n == input.size();
|
|
||||||
} catch(const std::invalid_argument &) {
|
|
||||||
return false;
|
|
||||||
} catch(const std::out_of_range &) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
char *val = nullptr;
|
||||||
|
auto output_ld = std::strtold(input.c_str(), &val);
|
||||||
|
output = static_cast<T>(output_ld);
|
||||||
|
return val == (input.c_str() + input.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// complex
|
/// complex
|
||||||
@ -932,8 +955,7 @@ template <typename T,
|
|||||||
enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
typename std::underlying_type<T>::type val;
|
typename std::underlying_type<T>::type val;
|
||||||
bool retval = detail::lexical_cast(input, val);
|
if(!integral_conversion(input, val)) {
|
||||||
if(!retval) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
output = static_cast<T>(val);
|
output = static_cast<T>(val);
|
||||||
@ -958,7 +980,7 @@ template <
|
|||||||
enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
int val;
|
int val;
|
||||||
if(lexical_cast(input, val)) {
|
if(integral_conversion(input, val)) {
|
||||||
output = T(val);
|
output = T(val);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -977,7 +999,7 @@ template <
|
|||||||
enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
int val;
|
int val;
|
||||||
if(lexical_cast(input, val)) {
|
if(integral_conversion(input, val)) {
|
||||||
output = T(val);
|
output = T(val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -966,14 +966,11 @@ class AsNumberWithUnit : public Validator {
|
|||||||
if(opts & CASE_INSENSITIVE) {
|
if(opts & CASE_INSENSITIVE) {
|
||||||
unit = detail::to_lower(unit);
|
unit = detail::to_lower(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool converted = detail::lexical_cast(input, num);
|
|
||||||
if(!converted) {
|
|
||||||
throw ValidationError(std::string("Value ") + input + " could not be converted to " +
|
|
||||||
detail::type_name<Number>());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(unit.empty()) {
|
if(unit.empty()) {
|
||||||
|
if(!detail::lexical_cast(input, num)) {
|
||||||
|
throw ValidationError(std::string("Value ") + input + " could not be converted to " +
|
||||||
|
detail::type_name<Number>());
|
||||||
|
}
|
||||||
// No need to modify input if no unit passed
|
// No need to modify input if no unit passed
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -987,12 +984,22 @@ class AsNumberWithUnit : public Validator {
|
|||||||
detail::generate_map(mapping, true));
|
detail::generate_map(mapping, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform safe multiplication
|
if(!input.empty()) {
|
||||||
bool ok = detail::checked_multiply(num, it->second);
|
bool converted = detail::lexical_cast(input, num);
|
||||||
if(!ok) {
|
if(!converted) {
|
||||||
throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
|
throw ValidationError(std::string("Value ") + input + " could not be converted to " +
|
||||||
" factor would cause number overflow. Use smaller value.");
|
detail::type_name<Number>());
|
||||||
|
}
|
||||||
|
// perform safe multiplication
|
||||||
|
bool ok = detail::checked_multiply(num, it->second);
|
||||||
|
if(!ok) {
|
||||||
|
throw ValidationError(detail::to_string(num) + " multiplied by " + unit +
|
||||||
|
" factor would cause number overflow. Use smaller value.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
num = static_cast<Number>(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
input = detail::to_string(num);
|
input = detail::to_string(num);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -911,6 +911,9 @@ TEST(Types, TypeName) {
|
|||||||
std::string float_name = CLI::detail::type_name<double>();
|
std::string float_name = CLI::detail::type_name<double>();
|
||||||
EXPECT_EQ("FLOAT", float_name);
|
EXPECT_EQ("FLOAT", float_name);
|
||||||
|
|
||||||
|
std::string char_name = CLI::detail::type_name<char>();
|
||||||
|
EXPECT_EQ("CHAR", char_name);
|
||||||
|
|
||||||
std::string vector_name = CLI::detail::type_name<std::vector<int>>();
|
std::string vector_name = CLI::detail::type_name<std::vector<int>>();
|
||||||
EXPECT_EQ("INT", vector_name);
|
EXPECT_EQ("INT", vector_name);
|
||||||
|
|
||||||
@ -1025,6 +1028,11 @@ TEST(Types, LexicalCastInt) {
|
|||||||
|
|
||||||
std::string extra_input = "912i";
|
std::string extra_input = "912i";
|
||||||
EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, y));
|
EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, y));
|
||||||
|
|
||||||
|
std::string empty_input{};
|
||||||
|
EXPECT_FALSE(CLI::detail::lexical_cast(empty_input, x_signed));
|
||||||
|
EXPECT_FALSE(CLI::detail::lexical_cast(empty_input, x_unsigned));
|
||||||
|
EXPECT_FALSE(CLI::detail::lexical_cast(empty_input, y_signed));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Types, LexicalCastDouble) {
|
TEST(Types, LexicalCastDouble) {
|
||||||
@ -1037,10 +1045,14 @@ TEST(Types, LexicalCastDouble) {
|
|||||||
EXPECT_FALSE(CLI::detail::lexical_cast(bad_input, x));
|
EXPECT_FALSE(CLI::detail::lexical_cast(bad_input, x));
|
||||||
|
|
||||||
std::string overflow_input = "1" + std::to_string(LDBL_MAX);
|
std::string overflow_input = "1" + std::to_string(LDBL_MAX);
|
||||||
EXPECT_FALSE(CLI::detail::lexical_cast(overflow_input, x));
|
EXPECT_TRUE(CLI::detail::lexical_cast(overflow_input, x));
|
||||||
|
EXPECT_FALSE(std::isfinite(x));
|
||||||
|
|
||||||
std::string extra_input = "9.12i";
|
std::string extra_input = "9.12i";
|
||||||
EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, x));
|
EXPECT_FALSE(CLI::detail::lexical_cast(extra_input, x));
|
||||||
|
|
||||||
|
std::string empty_input{};
|
||||||
|
EXPECT_FALSE(CLI::detail::lexical_cast(empty_input, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Types, LexicalCastBool) {
|
TEST(Types, LexicalCastBool) {
|
||||||
|
@ -167,6 +167,31 @@ TEST_F(TApp, BoolOption) {
|
|||||||
EXPECT_FALSE(bflag);
|
EXPECT_FALSE(bflag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, CharOption) {
|
||||||
|
char c1{'t'};
|
||||||
|
app.add_option("-c", c1);
|
||||||
|
|
||||||
|
args = {"-c", "g"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(c1, 'g');
|
||||||
|
|
||||||
|
args = {"-c", "1"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(c1, '1');
|
||||||
|
|
||||||
|
args = {"-c", "77"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(c1, 77);
|
||||||
|
|
||||||
|
// convert hex for digit
|
||||||
|
args = {"-c", "0x44"};
|
||||||
|
run();
|
||||||
|
EXPECT_EQ(c1, 0x44);
|
||||||
|
|
||||||
|
args = {"-c", "751615654161688126132138844896646748852"};
|
||||||
|
EXPECT_THROW(run(), CLI::ConversionError);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, vectorDefaults) {
|
TEST_F(TApp, vectorDefaults) {
|
||||||
std::vector<int> vals{4, 5};
|
std::vector<int> vals{4, 5};
|
||||||
auto opt = app.add_option("--long", vals, "", true);
|
auto opt = app.add_option("--long", vals, "", true);
|
||||||
|
@ -218,6 +218,7 @@ TEST_F(TApp, BoostOptionalEnumTest) {
|
|||||||
|
|
||||||
enum class eval : char { val0 = 0, val1 = 1, val2 = 2, val3 = 3, val4 = 4 };
|
enum class eval : char { val0 = 0, val1 = 1, val2 = 2, val3 = 3, val4 = 4 };
|
||||||
boost::optional<eval> opt, opt2;
|
boost::optional<eval> opt, opt2;
|
||||||
|
|
||||||
auto optptr = app.add_option<decltype(opt), eval>("-v,--val", opt);
|
auto optptr = app.add_option<decltype(opt), eval>("-v,--val", opt);
|
||||||
app.add_option_no_stream("-e,--eval", opt2);
|
app.add_option_no_stream("-e,--eval", opt2);
|
||||||
optptr->capture_default_str();
|
optptr->capture_default_str();
|
||||||
|
@ -692,7 +692,8 @@ TEST_F(TApp, NumberWithUnitBadInput) {
|
|||||||
args = {"-n", "13 c"};
|
args = {"-n", "13 c"};
|
||||||
EXPECT_THROW(run(), CLI::ValidationError);
|
EXPECT_THROW(run(), CLI::ValidationError);
|
||||||
args = {"-n", "a"};
|
args = {"-n", "a"};
|
||||||
EXPECT_THROW(run(), CLI::ValidationError);
|
// Assume 1.0 unit
|
||||||
|
EXPECT_NO_THROW(run());
|
||||||
args = {"-n", "12.0a"};
|
args = {"-n", "12.0a"};
|
||||||
EXPECT_THROW(run(), CLI::ValidationError);
|
EXPECT_THROW(run(), CLI::ValidationError);
|
||||||
args = {"-n", "a5"};
|
args = {"-n", "a5"};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user