mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
Add classification type traits (#286)
This cleans up the type checking a bit and makes it more readable, along with some other cleanup. * start work on trying to clean up the type traits for which lexical cast overload to use * fix readme issue and make the condition tests a little clearer * add a check for out of range errors on boolean conversions * Fix capitalization and some comments on option functions * fix a few code analysis warnings for VS2019
This commit is contained in:
parent
ba7aac9c8a
commit
17ddce2fb2
@ -604,7 +604,7 @@ The subcommand method
|
|||||||
.add_option_group(name,description)
|
.add_option_group(name,description)
|
||||||
```
|
```
|
||||||
|
|
||||||
Will create an option group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range test](./tests/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
|
Will create an option group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range example](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ogroup->add_option(option_pointer);
|
ogroup->add_option(option_pointer);
|
||||||
|
@ -1292,7 +1292,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
args.reserve(static_cast<size_t>(argc - 1));
|
args.reserve(static_cast<size_t>(argc) - 1);
|
||||||
for(int i = argc - 1; i > 0; i--)
|
for(int i = argc - 1; i > 0; i--)
|
||||||
args.emplace_back(argv[i]);
|
args.emplace_back(argv[i]);
|
||||||
parse(std::move(args));
|
parse(std::move(args));
|
||||||
@ -2317,7 +2317,7 @@ class App {
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
default:
|
default:
|
||||||
HorribleError("unrecognized classifier (you should not see this!)");
|
throw HorribleError("unrecognized classifier (you should not see this!)");
|
||||||
// LCOV_EXCL_END
|
// LCOV_EXCL_END
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -527,7 +527,7 @@ class Option : public OptionBase<Option> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// disable flag overrides
|
/// Disable flag overrides values, e.g. --flag=<value> is not allowed
|
||||||
Option *disable_flag_override(bool value = true) {
|
Option *disable_flag_override(bool value = true) {
|
||||||
disable_flag_override_ = value;
|
disable_flag_override_ = value;
|
||||||
return this;
|
return this;
|
||||||
@ -564,7 +564,7 @@ class Option : public OptionBase<Option> {
|
|||||||
/// Get the short names
|
/// Get the short names
|
||||||
const std::vector<std::string> get_snames() const { return snames_; }
|
const std::vector<std::string> get_snames() const { return snames_; }
|
||||||
|
|
||||||
/// get the flag names with specified default values
|
/// Get the flag names with specified default values
|
||||||
const std::vector<std::string> get_fnames() const { return fnames_; }
|
const std::vector<std::string> get_fnames() const { return fnames_; }
|
||||||
|
|
||||||
/// The number of times the option expects to be included
|
/// The number of times the option expects to be included
|
||||||
@ -790,6 +790,7 @@ class Option : public OptionBase<Option> {
|
|||||||
return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0);
|
return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the value that goes for a flag, nominally gets the default value but allows for overrides if not disabled
|
||||||
std::string get_flag_value(std::string name, std::string input_value) const {
|
std::string get_flag_value(std::string name, std::string input_value) const {
|
||||||
static const std::string trueString{"true"};
|
static const std::string trueString{"true"};
|
||||||
static const std::string falseString{"false"};
|
static const std::string falseString{"false"};
|
||||||
@ -855,7 +856,7 @@ class Option : public OptionBase<Option> {
|
|||||||
/// Get a copy of the results
|
/// Get a copy of the results
|
||||||
std::vector<std::string> results() const { return results_; }
|
std::vector<std::string> results() const { return results_; }
|
||||||
|
|
||||||
/// get the results as a particular type
|
/// Get the results as a specified type
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy>
|
enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy>
|
||||||
void results(T &output) const {
|
void results(T &output) const {
|
||||||
@ -884,7 +885,7 @@ class Option : public OptionBase<Option> {
|
|||||||
throw ConversionError(get_name(), results_);
|
throw ConversionError(get_name(), results_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// get the results as a vector of a particular type
|
/// Get the results as a vector of the specified type
|
||||||
template <typename T> void results(std::vector<T> &output) const {
|
template <typename T> void results(std::vector<T> &output) const {
|
||||||
output.clear();
|
output.clear();
|
||||||
bool retval = true;
|
bool retval = true;
|
||||||
@ -899,7 +900,7 @@ class Option : public OptionBase<Option> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return the results as a particular type
|
/// Return the results as the specified type
|
||||||
template <typename T> T as() const {
|
template <typename T> T as() const {
|
||||||
T output;
|
T output;
|
||||||
results(output);
|
results(output);
|
||||||
@ -980,7 +981,7 @@ class Option : public OptionBase<Option> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// run through the validators
|
// Run a result through the validators
|
||||||
std::string _validate(std::string &result) {
|
std::string _validate(std::string &result) {
|
||||||
std::string err_msg;
|
std::string err_msg;
|
||||||
for(const auto &vali : validators_) {
|
for(const auto &vali : validators_) {
|
||||||
@ -995,6 +996,7 @@ class Option : public OptionBase<Option> {
|
|||||||
return err_msg;
|
return err_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a single result to the result set, taking into account delimiters
|
||||||
int _add_result(std::string &&result) {
|
int _add_result(std::string &&result) {
|
||||||
int result_count = 0;
|
int result_count = 0;
|
||||||
if(delimiter_ == '\0') {
|
if(delimiter_ == '\0') {
|
||||||
|
@ -228,6 +228,118 @@ std::string checked_to_string(T &&) {
|
|||||||
return std::string{};
|
return std::string{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enumeration of the different supported categorizations of objects
|
||||||
|
enum objCategory : int {
|
||||||
|
integral_value = 2,
|
||||||
|
unsigned_integral = 4,
|
||||||
|
enumeration = 6,
|
||||||
|
boolean_value = 8,
|
||||||
|
floating_point = 10,
|
||||||
|
number_constructible = 12,
|
||||||
|
double_constructible = 14,
|
||||||
|
integer_constructible = 16,
|
||||||
|
vector_value = 30,
|
||||||
|
// string assignable or greater used in a condition so anything string like must come last
|
||||||
|
string_assignable = 50,
|
||||||
|
string_constructible = 60,
|
||||||
|
other = 200,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/// some type that is not otherwise recognized
|
||||||
|
template <typename T, typename Enable = void> struct classify_object { static constexpr objCategory value{other}; };
|
||||||
|
|
||||||
|
/// Set of overloads to classify an object according to type
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
|
||||||
|
!is_bool<T>::value && !std::is_enum<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{integral_value};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Unsigned integers
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<
|
||||||
|
T,
|
||||||
|
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{unsigned_integral};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Boolean values
|
||||||
|
template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{boolean_value};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Floats
|
||||||
|
template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{floating_point};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// String and similar direct assignment
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<
|
||||||
|
T,
|
||||||
|
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
|
std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{string_assignable};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// String and similar constructible and copy assignment
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<
|
||||||
|
T,
|
||||||
|
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
|
!std::is_assignable<T &, std::string>::value &&
|
||||||
|
std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{string_constructible};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Enumerations
|
||||||
|
template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||||
|
static constexpr objCategory value{enumeration};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
|
||||||
|
/// vectors, and enumerations
|
||||||
|
template <typename T> struct uncommon_type {
|
||||||
|
using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
|
!std::is_assignable<T &, std::string>::value &&
|
||||||
|
!std::is_constructible<T, std::string>::value && !is_vector<T>::value &&
|
||||||
|
!std::is_enum<T>::value,
|
||||||
|
std::true_type,
|
||||||
|
std::false_type>::type;
|
||||||
|
static const bool value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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> {
|
||||||
|
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> {
|
||||||
|
static const 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> {
|
||||||
|
static const objCategory value{double_constructible};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// vector type
|
||||||
|
template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> {
|
||||||
|
static const objCategory value{vector_value};
|
||||||
|
};
|
||||||
|
|
||||||
// Type name print
|
// Type name print
|
||||||
|
|
||||||
/// Was going to be based on
|
/// Was going to be based on
|
||||||
@ -235,38 +347,45 @@ std::string checked_to_string(T &&) {
|
|||||||
/// But this is cleaner and works better in this case
|
/// But this is cleaner and works better in this case
|
||||||
|
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == integral_value || classify_object<T>::value == integer_constructible,
|
||||||
|
detail::enabler> = detail::dummy>
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "INT";
|
return "INT";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
|
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "UINT";
|
return "UINT";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
|
template <
|
||||||
|
typename T,
|
||||||
|
enable_if_t<classify_object<T>::value == floating_point || classify_object<T>::value == number_constructible ||
|
||||||
|
classify_object<T>::value == double_constructible,
|
||||||
|
detail::enabler> = detail::dummy>
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "FLOAT";
|
return "FLOAT";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This one should not be used, since vector types print the internal type
|
/// This one should not be used, since vector types print the internal type
|
||||||
template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<classify_object<T>::value == vector_value, detail::enabler> = detail::dummy>
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "VECTOR";
|
return "VECTOR";
|
||||||
}
|
}
|
||||||
/// Print name for enumeration types
|
/// Print name for enumeration types
|
||||||
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy>
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "ENUM";
|
return "ENUM";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print name for enumeration types
|
||||||
|
template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
|
||||||
|
constexpr const char *type_name() {
|
||||||
|
return "BOOLEAN";
|
||||||
|
}
|
||||||
|
|
||||||
/// Print for all other types
|
/// Print for all other types
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value >= string_assignable, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value &&
|
|
||||||
!std::is_enum<T>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
constexpr const char *type_name() {
|
constexpr const char *type_name() {
|
||||||
return "TEXT";
|
return "TEXT";
|
||||||
}
|
}
|
||||||
@ -286,6 +405,9 @@ inline int64_t to_flag_value(std::string val) {
|
|||||||
val = detail::to_lower(val);
|
val = detail::to_lower(val);
|
||||||
int64_t ret;
|
int64_t ret;
|
||||||
if(val.size() == 1) {
|
if(val.size() == 1) {
|
||||||
|
if(val[0] >= '1' && val[0] <= '9') {
|
||||||
|
return (static_cast<int64_t>(val[0]) - '0');
|
||||||
|
}
|
||||||
switch(val[0]) {
|
switch(val[0]) {
|
||||||
case '0':
|
case '0':
|
||||||
case 'f':
|
case 'f':
|
||||||
@ -293,22 +415,11 @@ inline int64_t to_flag_value(std::string val) {
|
|||||||
case '-':
|
case '-':
|
||||||
ret = -1;
|
ret = -1;
|
||||||
break;
|
break;
|
||||||
case '1':
|
|
||||||
case 't':
|
case 't':
|
||||||
case 'y':
|
case 'y':
|
||||||
case '+':
|
case '+':
|
||||||
ret = 1;
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
ret = val[0] - '0';
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw std::invalid_argument("unrecognized character");
|
throw std::invalid_argument("unrecognized character");
|
||||||
}
|
}
|
||||||
@ -325,10 +436,7 @@ inline int64_t to_flag_value(std::string val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Signed integers
|
/// Signed integers
|
||||||
template <
|
template <typename T, enable_if_t<classify_object<T>::value == integral_value, detail::enabler> = detail::dummy>
|
||||||
typename T,
|
|
||||||
enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value && !is_bool<T>::value && !std::is_enum<T>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
try {
|
try {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
@ -343,9 +451,7 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unsigned integers
|
/// Unsigned integers
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::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.empty() && input.front() == '-')
|
||||||
return false; // std::stoull happily converts negative values to junk without any errors.
|
return false; // std::stoull happily converts negative values to junk without any errors.
|
||||||
@ -363,7 +469,7 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Boolean values
|
/// Boolean values
|
||||||
template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy>
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
try {
|
try {
|
||||||
auto out = to_flag_value(input);
|
auto out = to_flag_value(input);
|
||||||
@ -371,11 +477,16 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
return true;
|
return true;
|
||||||
} catch(const std::invalid_argument &) {
|
} catch(const std::invalid_argument &) {
|
||||||
return false;
|
return false;
|
||||||
|
} catch(const std::out_of_range &) {
|
||||||
|
// if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still
|
||||||
|
// valid all we care about the sign
|
||||||
|
output = (input[0] != '-');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Floats
|
/// Floats
|
||||||
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<classify_object<T>::value == 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 {
|
try {
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
@ -389,27 +500,21 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// String and similar direct assignment
|
/// String and similar direct assignment
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == string_assignable, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
std::is_assignable<T &, std::string>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
output = input;
|
output = input;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// String and similar constructible and copy assignment
|
/// String and similar constructible and copy assignment
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == string_constructible, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
!std::is_assignable<T &, std::string>::value && std::is_constructible<T, std::string>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
output = T(input);
|
output = T(input);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerations
|
/// Enumerations
|
||||||
template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
|
template <typename T, enable_if_t<classify_object<T>::value == 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);
|
bool retval = detail::lexical_cast(input, val);
|
||||||
@ -421,12 +526,7 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Assignable from double or int
|
/// Assignable from double or int
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == number_constructible, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
!std::is_assignable<T &, std::string>::value &&
|
|
||||||
!std::is_constructible<T, std::string>::value && !std::is_enum<T>::value &&
|
|
||||||
is_direct_constructible<T, double>::value && is_direct_constructible<T, int>::value,
|
|
||||||
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(lexical_cast(input, val)) {
|
||||||
@ -442,13 +542,8 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
return from_stream(input, output);
|
return from_stream(input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assignable from int64
|
/// Assignable from int
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == integer_constructible, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
!std::is_assignable<T &, std::string>::value &&
|
|
||||||
!std::is_constructible<T, std::string>::value && !std::is_enum<T>::value &&
|
|
||||||
!is_direct_constructible<T, double>::value && is_direct_constructible<T, int>::value,
|
|
||||||
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(lexical_cast(input, val)) {
|
||||||
@ -459,12 +554,7 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Assignable from double
|
/// Assignable from double
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == double_constructible, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
!std::is_assignable<T &, std::string>::value &&
|
|
||||||
!std::is_constructible<T, std::string>::value && !std::is_enum<T>::value &&
|
|
||||||
is_direct_constructible<T, double>::value && !is_direct_constructible<T, int>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
double val;
|
double val;
|
||||||
if(lexical_cast(input, val)) {
|
if(lexical_cast(input, val)) {
|
||||||
@ -475,15 +565,10 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Non-string parsable by a stream
|
/// Non-string parsable by a stream
|
||||||
template <typename T,
|
template <typename T, enable_if_t<classify_object<T>::value == other, detail::enabler> = detail::dummy>
|
||||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
|
||||||
!std::is_assignable<T &, std::string>::value &&
|
|
||||||
!std::is_constructible<T, std::string>::value && !std::is_enum<T>::value &&
|
|
||||||
!is_direct_constructible<T, double>::value && !is_direct_constructible<T, int>::value,
|
|
||||||
detail::enabler> = detail::dummy>
|
|
||||||
bool lexical_cast(const std::string &input, T &output) {
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
static_assert(is_istreamable<T>::value,
|
static_assert(is_istreamable<T>::value,
|
||||||
"option object type must have a lexical cast overload or streaming input operator(>>) defined if it "
|
"option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
|
||||||
"is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
|
"is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
|
||||||
return from_stream(input, output);
|
return from_stream(input, output);
|
||||||
}
|
}
|
||||||
|
@ -647,6 +647,15 @@ TEST_F(TApp, BoolOption) {
|
|||||||
args = {"-b", "-7"};
|
args = {"-b", "-7"};
|
||||||
run();
|
run();
|
||||||
EXPECT_FALSE(bflag);
|
EXPECT_FALSE(bflag);
|
||||||
|
|
||||||
|
// cause an out of bounds error internally
|
||||||
|
args = {"-b", "751615654161688126132138844896646748852"};
|
||||||
|
run();
|
||||||
|
EXPECT_TRUE(bflag);
|
||||||
|
|
||||||
|
args = {"-b", "-751615654161688126132138844896646748852"};
|
||||||
|
run();
|
||||||
|
EXPECT_FALSE(bflag);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TApp, ShortOpts) {
|
TEST_F(TApp, ShortOpts) {
|
||||||
|
@ -622,7 +622,7 @@ TEST_F(ManyGroups, Moving) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ManyGroupsPreTrigger : public ManyGroups {
|
struct ManyGroupsPreTrigger : public ManyGroups {
|
||||||
size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u};
|
size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u};
|
||||||
ManyGroupsPreTrigger() {
|
ManyGroupsPreTrigger() {
|
||||||
remove_required();
|
remove_required();
|
||||||
app.preparse_callback([this](size_t count) { triggerMain = count; });
|
app.preparse_callback([this](size_t count) { triggerMain = count; });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user