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

fix some warnings generated from klocwork static analyzer (#350)

* fix some warnings generated from klocwork static analyzer

* Some more visual studio static analyzer and clang-tidy fixes

* some formatting updates
This commit is contained in:
Philip Top 2019-11-29 05:54:32 -08:00 committed by Henry Schreiner
parent d621658e6d
commit 5f696596d7
7 changed files with 128 additions and 98 deletions

View File

@ -751,10 +751,11 @@ class App {
std::string flag_description = "") { std::string flag_description = "") {
CLI::callback_t fun = [function](const CLI::results_t &res) { CLI::callback_t fun = [function](const CLI::results_t &res) {
bool trigger; bool trigger{false};
auto result = CLI::detail::lexical_cast(res[0], trigger); auto result = CLI::detail::lexical_cast(res[0], trigger);
if(trigger) if(result && trigger) {
function(); function();
}
return result; return result;
}; };
return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description)); return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));

View File

@ -311,7 +311,7 @@ template <typename T> struct expected_count<T, typename std::enable_if<is_vector
}; };
// Enumeration of the different supported categorizations of objects // Enumeration of the different supported categorizations of objects
enum objCategory : int { enum class object_category : int {
integral_value = 2, integral_value = 2,
unsigned_integral = 4, unsigned_integral = 4,
enumeration = 6, enumeration = 6,
@ -330,14 +330,16 @@ enum objCategory : int {
}; };
/// some type that is not otherwise recognized /// some type that is not otherwise recognized
template <typename T, typename Enable = void> struct classify_object { static constexpr objCategory value{other}; }; template <typename T, typename Enable = void> struct classify_object {
static constexpr object_category value{object_category::other};
};
/// Set of overloads to classify an object according to type /// Set of overloads to classify an object according to type
template <typename T> template <typename T>
struct classify_object<T, struct classify_object<T,
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value && typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value &&
!is_bool<T>::value && !std::is_enum<T>::value>::type> { !is_bool<T>::value && !std::is_enum<T>::value>::type> {
static constexpr objCategory value{integral_value}; static constexpr object_category value{object_category::integral_value};
}; };
/// Unsigned integers /// Unsigned integers
@ -345,17 +347,17 @@ 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 && !is_bool<T>::value>::type> { 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}; static constexpr object_category value{object_category::unsigned_integral};
}; };
/// 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 objCategory value{boolean_value}; static constexpr object_category value{object_category::boolean_value};
}; };
/// Floats /// Floats
template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
static constexpr objCategory value{floating_point}; static constexpr object_category value{object_category::floating_point};
}; };
/// String and similar direct assignment /// String and similar direct assignment
@ -364,7 +366,7 @@ struct classify_object<
T, T,
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value && 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> { std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> {
static constexpr objCategory value{string_assignable}; static constexpr object_category value{object_category::string_assignable};
}; };
/// String and similar constructible and copy assignment /// String and similar constructible and copy assignment
@ -374,12 +376,12 @@ struct classify_object<
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value && typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
!std::is_assignable<T &, std::string>::value && !std::is_assignable<T &, std::string>::value &&
std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> { std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> {
static constexpr objCategory value{string_constructible}; static constexpr object_category value{object_category::string_constructible};
}; };
/// Enumerations /// Enumerations
template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> { template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
static constexpr objCategory value{enumeration}; static constexpr object_category value{object_category::enumeration};
}; };
/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
@ -400,7 +402,7 @@ struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
is_direct_constructible<T, double>::value && is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> { is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{number_constructible}; static constexpr object_category value{object_category::number_constructible};
}; };
/// Assignable from int /// Assignable from int
@ -409,7 +411,7 @@ struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
!is_direct_constructible<T, double>::value && !is_direct_constructible<T, double>::value &&
is_direct_constructible<T, int>::value>::type> { is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{integer_constructible}; static constexpr object_category value{object_category::integer_constructible};
}; };
/// Assignable from double /// Assignable from double
@ -418,7 +420,7 @@ struct classify_object<T,
typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 && typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
is_direct_constructible<T, double>::value && is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value>::type> { !is_direct_constructible<T, int>::value>::type> {
static constexpr objCategory value{double_constructible}; static constexpr object_category value{object_category::double_constructible};
}; };
/// Tuple type /// Tuple type
@ -428,12 +430,12 @@ struct classify_object<T,
(is_tuple_like<T>::value && uncommon_type<T>::value && (is_tuple_like<T>::value && uncommon_type<T>::value &&
!is_direct_constructible<T, double>::value && !is_direct_constructible<T, double>::value &&
!is_direct_constructible<T, int>::value)>::type> { !is_direct_constructible<T, int>::value)>::type> {
static constexpr objCategory value{tuple_value}; static constexpr object_category value{object_category::tuple_value};
}; };
/// Vector type /// Vector type
template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> { template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> {
static constexpr objCategory value{vector_value}; static constexpr object_category value{object_category::vector_value};
}; };
// Type name print // Type name print
@ -443,48 +445,53 @@ template <typename T> struct classify_object<T, typename std::enable_if<is_vecto
/// 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<classify_object<T>::value == integral_value || classify_object<T>::value == integer_constructible, enable_if_t<classify_object<T>::value == object_category::integral_value ||
classify_object<T>::value == object_category::integer_constructible,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "INT"; return "INT";
} }
template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "UINT"; return "UINT";
} }
template < template <typename T,
typename T, enable_if_t<classify_object<T>::value == object_category::floating_point ||
enable_if_t<classify_object<T>::value == floating_point || classify_object<T>::value == number_constructible || classify_object<T>::value == object_category::number_constructible ||
classify_object<T>::value == double_constructible, classify_object<T>::value == object_category::double_constructible,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "FLOAT"; return "FLOAT";
} }
/// Print name for enumeration types /// Print name for enumeration types
template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "ENUM"; return "ENUM";
} }
/// Print name for enumeration types /// Print name for enumeration types
template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "BOOLEAN"; return "BOOLEAN";
} }
/// Print for all other types /// Print for all other types
template <typename T, enable_if_t<classify_object<T>::value >= string_assignable, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value >= object_category::string_assignable, detail::enabler> = detail::dummy>
constexpr const char *type_name() { constexpr const char *type_name() {
return "TEXT"; return "TEXT";
} }
/// Print name for single element tuple types /// Print name for single element tuple types
template < template <typename T,
typename T, enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value == 1,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value == 1, detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
inline std::string type_name() { inline std::string type_name() {
return type_name<typename std::tuple_element<0, T>::type>(); return type_name<typename std::tuple_element<0, T>::type>();
} }
@ -505,9 +512,9 @@ template <typename T, std::size_t I>
} }
/// Print type name for tuples with 2 or more elements /// Print type name for tuples with 2 or more elements
template < template <typename T,
typename T, enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count<T>::value >= 2,
enable_if_t<classify_object<T>::value == tuple_value && type_count<T>::value >= 2, detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
std::string type_name() { std::string type_name() {
auto tname = std::string(1, '[') + tuple_name<T, 0>(); auto tname = std::string(1, '[') + tuple_name<T, 0>();
tname.push_back(']'); tname.push_back(']');
@ -515,7 +522,8 @@ std::string type_name() {
} }
/// This one should not be used normally, since vector types print the internal type /// This one should not be used normally, since vector types print the internal type
template <typename T, enable_if_t<classify_object<T>::value == vector_value, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::vector_value, detail::enabler> = detail::dummy>
inline std::string type_name() { inline std::string type_name() {
return type_name<typename T::value_type>(); return type_name<typename T::value_type>();
} }
@ -566,7 +574,8 @@ inline int64_t to_flag_value(std::string val) {
} }
/// Signed integers /// Signed integers
template <typename T, enable_if_t<classify_object<T>::value == integral_value, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::integral_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;
@ -581,7 +590,8 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Unsigned integers /// Unsigned integers
template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<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) {
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.
@ -599,7 +609,8 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Boolean values /// Boolean values
template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::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);
@ -616,7 +627,8 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Floats /// Floats
template <typename T, enable_if_t<classify_object<T>::value == floating_point, detail::enabler> = detail::dummy> template <typename T,
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 { try {
size_t n = 0; size_t n = 0;
@ -630,21 +642,25 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// String and similar direct assignment /// String and similar direct assignment
template <typename T, enable_if_t<classify_object<T>::value == string_assignable, detail::enabler> = detail::dummy> template <typename T,
enable_if_t<classify_object<T>::value == object_category::string_assignable, 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, enable_if_t<classify_object<T>::value == string_constructible, detail::enabler> = detail::dummy> template <
typename T,
enable_if_t<classify_object<T>::value == object_category::string_constructible, 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<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy> template <typename T,
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); bool retval = detail::lexical_cast(input, val);
@ -656,7 +672,9 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Assignable from double or int /// Assignable from double or int
template <typename T, enable_if_t<classify_object<T>::value == number_constructible, detail::enabler> = detail::dummy> template <
typename T,
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(lexical_cast(input, val)) {
@ -673,7 +691,9 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Assignable from int /// Assignable from int
template <typename T, enable_if_t<classify_object<T>::value == integer_constructible, detail::enabler> = detail::dummy> template <
typename T,
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(lexical_cast(input, val)) {
@ -684,7 +704,9 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Assignable from double /// Assignable from double
template <typename T, enable_if_t<classify_object<T>::value == double_constructible, detail::enabler> = detail::dummy> template <
typename T,
enable_if_t<classify_object<T>::value == object_category::double_constructible, 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)) {
@ -695,7 +717,7 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Non-string parsable by a stream /// Non-string parsable by a stream
template <typename T, enable_if_t<classify_object<T>::value == other, detail::enabler> = detail::dummy> template <typename T, enable_if_t<classify_object<T>::value == object_category::other, 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 "
@ -704,11 +726,12 @@ bool lexical_cast(const std::string &input, T &output) {
} }
/// Assign a value through lexical cast operations /// Assign a value through lexical cast operations
template <typename T, template <
typename XC, typename T,
enable_if_t<std::is_same<T, XC>::value && (classify_object<T>::value == string_assignable || typename XC,
classify_object<T>::value == string_constructible), enable_if_t<std::is_same<T, XC>::value && (classify_object<T>::value == object_category::string_assignable ||
detail::enabler> = detail::dummy> classify_object<T>::value == object_category::string_constructible),
detail::enabler> = detail::dummy>
bool lexical_assign(const std::string &input, T &output) { bool lexical_assign(const std::string &input, T &output) {
return lexical_cast(input, output); return lexical_cast(input, output);
} }
@ -716,8 +739,8 @@ bool lexical_assign(const std::string &input, T &output) {
/// Assign a value through lexical cast operations /// Assign a value through lexical cast operations
template <typename T, template <typename T,
typename XC, typename XC,
enable_if_t<std::is_same<T, XC>::value && classify_object<T>::value != string_assignable && enable_if_t<std::is_same<T, XC>::value && classify_object<T>::value != object_category::string_assignable &&
classify_object<T>::value != string_constructible, classify_object<T>::value != object_category::string_constructible,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_assign(const std::string &input, T &output) { bool lexical_assign(const std::string &input, T &output) {
if(input.empty()) { if(input.empty()) {
@ -774,7 +797,7 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
typename std::tuple_element<1, XC>::type v2; typename std::tuple_element<1, XC>::type v2;
bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1); bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[0], v1);
if(strings.size() > 1) { if(strings.size() > 1) {
retval &= lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2); retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[1], v2);
} }
if(retval) { if(retval) {
output = T{v1, v2}; output = T{v1, v2};
@ -789,15 +812,17 @@ template <class T,
expected_count<XC>::value == expected_max_vector_size && type_count<XC>::value == 1, expected_count<XC>::value == expected_max_vector_size && type_count<XC>::value == 1,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) { bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
bool retval = true;
output.clear(); output.clear();
output.reserve(strings.size()); output.reserve(strings.size());
for(const auto &elem : strings) { for(const auto &elem : strings) {
output.emplace_back(); output.emplace_back();
retval &= lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back()); bool retval = lexical_assign<typename T::value_type, typename XC::value_type>(elem, output.back());
if(!retval) {
return false;
}
} }
return (!output.empty()) && retval; return (!output.empty());
} }
/// Lexical conversion of a vector types with type size of two /// Lexical conversion of a vector types with type size of two
@ -807,15 +832,14 @@ template <class T,
expected_count<XC>::value == expected_max_vector_size && type_count<XC>::value == 2, expected_count<XC>::value == expected_max_vector_size && type_count<XC>::value == 2,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, T &output) { bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
bool retval = true;
output.clear(); output.clear();
for(size_t ii = 0; ii < strings.size(); ii += 2) { for(size_t ii = 0; ii < strings.size(); ii += 2) {
typename std::tuple_element<0, typename XC::value_type>::type v1; typename std::tuple_element<0, typename XC::value_type>::type v1;
typename std::tuple_element<1, typename XC::value_type>::type v2; typename std::tuple_element<1, typename XC::value_type>::type v2;
retval = lexical_assign<decltype(v1), decltype(v1)>(strings[ii], v1); bool retval = lexical_assign<decltype(v1), decltype(v1)>(strings[ii], v1);
if(strings.size() > ii + 1) { if(strings.size() > ii + 1) {
retval &= lexical_assign<decltype(v2), decltype(v2)>(strings[ii + 1], v2); retval = retval && lexical_assign<decltype(v2), decltype(v2)>(strings[ii + 1], v2);
} }
if(retval) { if(retval) {
output.emplace_back(v1, v2); output.emplace_back(v1, v2);
@ -823,7 +847,7 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
return false; return false;
} }
} }
return (!output.empty()) && retval; return (!output.empty());
} }
/// Conversion to a vector type using a particular single type as the conversion type /// Conversion to a vector type using a particular single type as the conversion type
@ -839,7 +863,7 @@ bool lexical_conversion(const std::vector<std ::string> &strings, T &output) {
for(const auto &elem : strings) { for(const auto &elem : strings) {
output.emplace_back(); output.emplace_back();
retval &= lexical_assign<typename T::value_type, XC>(elem, output.back()); retval = retval && lexical_assign<typename T::value_type, XC>(elem, output.back());
} }
return (!output.empty()) && retval; return (!output.empty()) && retval;
} }
@ -873,12 +897,12 @@ template <class T, class XC, std::size_t I>
I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) { I<type_count<T>::value, bool>::type tuple_conversion(const std::vector<std::string> &strings, T &output) {
bool retval = true; bool retval = true;
if(strings.size() > I) { if(strings.size() > I) {
retval &= lexical_assign< retval = retval && lexical_assign<typename std::tuple_element<I, T>::type,
typename std::tuple_element<I, T>::type, typename std::conditional<is_tuple_like<XC>::value,
typename std::conditional<is_tuple_like<XC>::value, typename std::tuple_element<I, XC>::type, XC>::type>( typename std::tuple_element<I, XC>::type,
strings[I], std::get<I>(output)); XC>::type>(strings[I], std::get<I>(output));
} }
retval &= tuple_conversion<T, XC, I + 1>(strings, output); retval = retval && tuple_conversion<T, XC, I + 1>(strings, output);
return retval; return retval;
} }

View File

@ -373,17 +373,16 @@ class IPV4Validator : public Validator {
func_ = [](std::string &ip_addr) { func_ = [](std::string &ip_addr) {
auto result = CLI::detail::split(ip_addr, '.'); auto result = CLI::detail::split(ip_addr, '.');
if(result.size() != 4) { if(result.size() != 4) {
return "Invalid IPV4 address must have four parts (" + ip_addr + ')'; return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
} }
int num; int num;
bool retval = true;
for(const auto &var : result) { for(const auto &var : result) {
retval &= detail::lexical_cast(var, num); bool retval = detail::lexical_cast(var, num);
if(!retval) { if(!retval) {
return "Failed parsing number (" + var + ')'; return std::string("Failed parsing number (") + var + ')';
} }
if(num < 0 || num > 255) { if(num < 0 || num > 255) {
return "Each IP number must be between 0 and 255 " + var; return std::string("Each IP number must be between 0 and 255 ") + var;
} }
} }
return std::string(); return std::string();
@ -398,10 +397,10 @@ class PositiveNumber : public Validator {
func_ = [](std::string &number_str) { func_ = [](std::string &number_str) {
double number; double number;
if(!detail::lexical_cast(number_str, number)) { if(!detail::lexical_cast(number_str, number)) {
return "Failed parsing number: (" + number_str + ')'; return std::string("Failed parsing number: (") + number_str + ')';
} }
if(number <= 0) { if(number <= 0) {
return "Number less or equal to 0: (" + number_str + ')'; return std::string("Number less or equal to 0: (") + number_str + ')';
} }
return std::string(); return std::string();
}; };
@ -414,10 +413,10 @@ class NonNegativeNumber : public Validator {
func_ = [](std::string &number_str) { func_ = [](std::string &number_str) {
double number; double number;
if(!detail::lexical_cast(number_str, number)) { if(!detail::lexical_cast(number_str, number)) {
return "Failed parsing number: (" + number_str + ')'; return std::string("Failed parsing number: (") + number_str + ')';
} }
if(number < 0) { if(number < 0) {
return "Number less than 0: (" + number_str + ')'; return std::string("Number less than 0: (") + number_str + ')';
} }
return std::string(); return std::string();
}; };
@ -431,7 +430,7 @@ class Number : public Validator {
func_ = [](std::string &number_str) { func_ = [](std::string &number_str) {
double number; double number;
if(!detail::lexical_cast(number_str, number)) { if(!detail::lexical_cast(number_str, number)) {
return "Failed parsing as a number (" + number_str + ')'; return std::string("Failed parsing as a number (") + number_str + ')';
} }
return std::string(); return std::string();
}; };
@ -482,7 +481,8 @@ class Range : public Validator {
T val; T val;
bool converted = detail::lexical_cast(input, val); bool converted = detail::lexical_cast(input, val);
if((!converted) || (val < min || val > max)) if((!converted) || (val < min || val > max))
return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max); return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " +
std::to_string(max);
return std::string(); return std::string();
}; };
@ -508,7 +508,7 @@ class Bound : public Validator {
T val; T val;
bool converted = detail::lexical_cast(input, val); bool converted = detail::lexical_cast(input, val);
if(!converted) { if(!converted) {
return "Value " + input + " could not be converted"; return std::string("Value ") + input + " could not be converted";
} }
if(val < min) if(val < min)
input = detail::to_string(min); input = detail::to_string(min);
@ -943,7 +943,8 @@ class AsNumberWithUnit : public Validator {
bool converted = detail::lexical_cast(input, num); bool converted = detail::lexical_cast(input, num);
if(!converted) { if(!converted) {
throw ValidationError("Value " + input + " could not be converted to " + detail::type_name<Number>()); throw ValidationError(std::string("Value ") + input + " could not be converted to " +
detail::type_name<Number>());
} }
if(unit.empty()) { if(unit.empty()) {
@ -991,7 +992,8 @@ class AsNumberWithUnit : public Validator {
for(auto &kv : mapping) { for(auto &kv : mapping) {
auto s = detail::to_lower(kv.first); auto s = detail::to_lower(kv.first);
if(lower_mapping.count(s)) { if(lower_mapping.count(s)) {
throw ValidationError("Several matching lowercase unit representations are found: " + s); throw ValidationError(std::string("Several matching lowercase unit representations are found: ") +
s);
} }
lower_mapping[detail::to_lower(kv.first)] = kv.second; lower_mapping[detail::to_lower(kv.first)] = kv.second;
} }

View File

@ -294,7 +294,7 @@ TEST_F(TApp, OneStringEqualVersionSingleString) {
TEST_F(TApp, OneStringEqualVersionSingleStringQuoted) { TEST_F(TApp, OneStringEqualVersionSingleStringQuoted) {
std::string str; std::string str;
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
app.parse("--string=\"this is my quoted string\""); app.parse(R"raw(--string="this is my quoted string")raw");
EXPECT_EQ(1u, app.count("-s")); EXPECT_EQ(1u, app.count("-s"));
EXPECT_EQ(1u, app.count("--string")); EXPECT_EQ(1u, app.count("--string"));
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
@ -305,7 +305,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultiple) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.parse("--string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"`"); app.parse(R"raw(--string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"`)raw");
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -316,12 +316,12 @@ TEST_F(TApp, OneStringEqualVersionSingleStringEmbeddedEqual) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.parse("--string=\"app=\\\"test1 b\\\" test2=\\\"frogs\\\"\" -t 'qstring 2' -m=`\"quoted string\"`"); app.parse(R"raw(--string="app=\"test1 b\" test2=\"frogs\"" -t 'qstring 2' -m=`"quoted string"`)raw");
EXPECT_EQ(str, "app=\"test1 b\" test2=\"frogs\""); EXPECT_EQ(str, "app=\"test1 b\" test2=\"frogs\"");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
app.parse("--string=\"app='test1 b' test2='frogs'\" -t 'qstring 2' -m=`\"quoted string\"`"); app.parse(R"raw(--string="app='test1 b' test2='frogs'" -t 'qstring 2' -m=`"quoted string"`)raw");
EXPECT_EQ(str, "app='test1 b' test2='frogs'"); EXPECT_EQ(str, "app='test1 b' test2='frogs'");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -333,12 +333,12 @@ TEST_F(TApp, OneStringEqualVersionSingleStringEmbeddedEqualWindowsStyle) {
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("--mstr", str3); app.add_option("--mstr", str3);
app.allow_windows_style_options(); app.allow_windows_style_options();
app.parse("/string:\"app:\\\"test1 b\\\" test2:\\\"frogs\\\"\" /t 'qstring 2' /mstr:`\"quoted string\"`"); app.parse(R"raw(/string:"app:\"test1 b\" test2:\"frogs\"" /t 'qstring 2' /mstr:`"quoted string"`)raw");
EXPECT_EQ(str, "app:\"test1 b\" test2:\"frogs\""); EXPECT_EQ(str, "app:\"test1 b\" test2:\"frogs\"");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
app.parse("/string:\"app:'test1 b' test2:'frogs'\" /t 'qstring 2' /mstr:`\"quoted string\"`"); app.parse(R"raw(/string:"app:'test1 b' test2:'frogs'" /t 'qstring 2' /mstr:`"quoted string"`)raw");
EXPECT_EQ(str, "app:'test1 b' test2:'frogs'"); EXPECT_EQ(str, "app:'test1 b' test2:'frogs'");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -350,7 +350,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleMixedStyle) {
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.allow_windows_style_options(); app.allow_windows_style_options();
app.parse("/string:\"this is my quoted string\" /t 'qstring 2' -m=`\"quoted string\"`"); app.parse(R"raw(/string:"this is my quoted string" /t 'qstring 2' -m=`"quoted string"`)raw");
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -361,7 +361,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleInMiddle) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.parse(R"raw(--string="this is my quoted string" -t "qst\"ring 2" -m=`"quoted string"`")raw"); app.parse(R"raw(--string="this is my quoted string" -t "qst\"ring 2" -m=`"quoted string"`)raw");
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
EXPECT_EQ(str2, "qst\"ring 2"); EXPECT_EQ(str2, "qst\"ring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -384,7 +384,7 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleWithEqual) {
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.add_option("-j,--jstr", str4); app.add_option("-j,--jstr", str4);
app.parse("--string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"` --jstr=Unquoted"); app.parse(R"raw(--string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"` --jstr=Unquoted)raw");
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");
@ -397,8 +397,9 @@ TEST_F(TApp, OneStringEqualVersionSingleStringQuotedMultipleWithEqualAndProgram)
app.add_option("-t,--tstr", str2); app.add_option("-t,--tstr", str2);
app.add_option("-m,--mstr", str3); app.add_option("-m,--mstr", str3);
app.add_option("-j,--jstr", str4); app.add_option("-j,--jstr", str4);
app.parse("program --string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"` --jstr=Unquoted", app.parse(
true); R"raw(program --string="this is my quoted string" -t 'qstring 2' -m=`"quoted string"` --jstr=Unquoted)raw",
true);
EXPECT_EQ(str, "this is my quoted string"); EXPECT_EQ(str, "this is my quoted string");
EXPECT_EQ(str2, "qstring 2"); EXPECT_EQ(str2, "qstring 2");
EXPECT_EQ(str3, "\"quoted string\""); EXPECT_EQ(str3, "\"quoted string\"");

View File

@ -704,7 +704,7 @@ class Unstreamable {
int x_ = -1; int x_ = -1;
public: public:
Unstreamable() {} Unstreamable() = default;
int get_x() const { return x_; } int get_x() const { return x_; }
void set_x(int x) { x_ = x; } void set_x(int x) { x_ = x; }
}; };
@ -719,7 +719,8 @@ std::istream &operator>>(std::istream &in, Unstreamable &value) {
return in; return in;
} }
// these need to be different classes otherwise the definitions conflict // these need to be different classes otherwise the definitions conflict
static_assert(CLI::detail::is_istreamable<Unstreamable>::value, "Unstreamable type is still unstreamable"); static_assert(CLI::detail::is_istreamable<Unstreamable>::value,
"Unstreamable type is still unstreamable and it should be");
TEST_F(TApp, MakeUnstreamableOptions) { TEST_F(TApp, MakeUnstreamableOptions) {
Unstreamable value; Unstreamable value;

View File

@ -859,7 +859,7 @@ TEST(Types, TypeName) {
EXPECT_EQ("FLOAT", vector_name); EXPECT_EQ("FLOAT", vector_name);
static_assert(CLI::detail::classify_object<std::pair<int, std::string>>::value == static_assert(CLI::detail::classify_object<std::pair<int, std::string>>::value ==
CLI::detail::objCategory::tuple_value, CLI::detail::object_category::tuple_value,
"pair<int,string> does not read like a tuple"); "pair<int,string> does not read like a tuple");
std::string pair_name = CLI::detail::type_name<std::vector<std::pair<int, std::string>>>(); std::string pair_name = CLI::detail::type_name<std::vector<std::pair<int, std::string>>>();
@ -869,16 +869,16 @@ TEST(Types, TypeName) {
EXPECT_EQ("UINT", vector_name); EXPECT_EQ("UINT", vector_name);
auto vclass = CLI::detail::classify_object<std::vector<std::vector<unsigned char>>>::value; auto vclass = CLI::detail::classify_object<std::vector<std::vector<unsigned char>>>::value;
EXPECT_EQ(vclass, CLI::detail::objCategory::vector_value); EXPECT_EQ(vclass, CLI::detail::object_category::vector_value);
auto tclass = CLI::detail::classify_object<std::tuple<double>>::value; auto tclass = CLI::detail::classify_object<std::tuple<double>>::value;
EXPECT_EQ(tclass, CLI::detail::objCategory::number_constructible); EXPECT_EQ(tclass, CLI::detail::object_category::number_constructible);
std::string tuple_name = CLI::detail::type_name<std::tuple<double>>(); std::string tuple_name = CLI::detail::type_name<std::tuple<double>>();
EXPECT_EQ("FLOAT", tuple_name); EXPECT_EQ("FLOAT", tuple_name);
static_assert(CLI::detail::classify_object<std::tuple<int, std::string>>::value == static_assert(CLI::detail::classify_object<std::tuple<int, std::string>>::value ==
CLI::detail::objCategory::tuple_value, CLI::detail::object_category::tuple_value,
"tuple<int,string> does not read like a tuple"); "tuple<int,string> does not read like a tuple");
tuple_name = CLI::detail::type_name<std::tuple<int, std::string>>(); tuple_name = CLI::detail::type_name<std::tuple<int, std::string>>();
EXPECT_EQ("[INT,TEXT]", tuple_name); EXPECT_EQ("[INT,TEXT]", tuple_name);
@ -906,8 +906,8 @@ TEST(Types, TypeName) {
EXPECT_EQ("ENUM", enum_name); EXPECT_EQ("ENUM", enum_name);
vclass = CLI::detail::classify_object<std::tuple<test>>::value; vclass = CLI::detail::classify_object<std::tuple<test>>::value;
EXPECT_EQ(vclass, CLI::detail::objCategory::tuple_value); EXPECT_EQ(vclass, CLI::detail::object_category::tuple_value);
static_assert(CLI::detail::classify_object<std::tuple<test>>::value == CLI::detail::objCategory::tuple_value, static_assert(CLI::detail::classify_object<std::tuple<test>>::value == CLI::detail::object_category::tuple_value,
"tuple<test> does not classify as a tuple"); "tuple<test> does not classify as a tuple");
std::string enum_name2 = CLI::detail::type_name<std::tuple<test>>(); std::string enum_name2 = CLI::detail::type_name<std::tuple<test>>();
EXPECT_EQ("ENUM", enum_name2); EXPECT_EQ("ENUM", enum_name2);

View File

@ -198,6 +198,7 @@ TEST_F(TApp, BuiltinComplexSingleImag) {
EXPECT_DOUBLE_EQ(0, comp.imag()); EXPECT_DOUBLE_EQ(0, comp.imag());
} }
/// Simple class containing two strings useful for testing lexical cast and conversions
class spair { class spair {
public: public:
spair() = default; spair() = default;