// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner // under NSF AWARD 1414736 and by the respective contributors. // All rights reserved. // // SPDX-License-Identifier: BSD-3-Clause #pragma once // [CLI11:public_includes:set] #include #include #include #include #include #include #include #include // [CLI11:public_includes:end] #include "StringTools.hpp" namespace CLI { // [CLI11:type_tools_hpp:verbatim] // Type tools // Utilities for type enabling namespace detail { // Based generally on https://rmf.io/cxx11/almost-static-if /// Simple empty scoped class enum class enabler {}; /// An instance to use in EnableIf constexpr enabler dummy = {}; } // namespace detail /// A copy of enable_if_t from C++14, compatible with C++11. /// /// We could check to see if C++14 is being used, but it does not hurt to redefine this /// (even Google does this: https://github.com/google/skia/blob/main/include/private/SkTLogic.h) /// It is not in the std namespace anyway, so no harm done. template using enable_if_t = typename std::enable_if::type; /// A copy of std::void_t from C++17 (helper for C++11 and C++14) template struct make_void { using type = void; }; /// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine template using void_t = typename make_void::type; /// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine template using conditional_t = typename std::conditional::type; /// Check to see if something is bool (fail check by default) template struct is_bool : std::false_type {}; /// Check to see if something is bool (true if actually a bool) template <> struct is_bool : std::true_type {}; /// Check to see if something is a shared pointer template struct is_shared_ptr : std::false_type {}; /// Check to see if something is a shared pointer (True if really a shared pointer) template struct is_shared_ptr> : std::true_type {}; /// Check to see if something is a shared pointer (True if really a shared pointer) template struct is_shared_ptr> : std::true_type {}; /// Check to see if something is copyable pointer template struct is_copyable_ptr { static bool const value = is_shared_ptr::value || std::is_pointer::value; }; /// This can be specialized to override the type deduction for IsMember. template struct IsMemberType { using type = T; }; /// The main custom type needed here is const char * should be a string. template <> struct IsMemberType { using type = std::string; }; namespace detail { // These are utilities for IsMember and other transforming objects /// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that /// pointer_traits be valid. /// not a pointer template struct element_type { using type = T; }; template struct element_type::value>::type> { using type = typename std::pointer_traits::element_type; }; /// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of /// the container template struct element_value_type { using type = typename element_type::type::value_type; }; /// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. template struct pair_adaptor : std::false_type { using value_type = typename T::value_type; using first_type = typename std::remove_const::type; using second_type = typename std::remove_const::type; /// Get the first value (really just the underlying value) template static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { return std::forward(pair_value); } /// Get the second value (really just the underlying value) template static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { return std::forward(pair_value); } }; /// Adaptor for map-like structure (true version, must have key_type and mapped_type). /// This wraps a mapped container in a few utilities access it in a general way. template struct pair_adaptor< T, conditional_t, void>> : std::true_type { using value_type = typename T::value_type; using first_type = typename std::remove_const::type; using second_type = typename std::remove_const::type; /// Get the first value (really just the underlying value) template static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward(pair_value))) { return std::get<0>(std::forward(pair_value)); } /// Get the second value (really just the underlying value) template static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward(pair_value))) { return std::get<1>(std::forward(pair_value)); } }; // Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning // in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in // brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a // little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. // But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be // suppressed #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnarrowing" #endif // check for constructibility from a specific type and copy assignable used in the parse detection template class is_direct_constructible { template static auto test(int, std::true_type) -> decltype( // NVCC warns about narrowing conversions here #ifdef __CUDACC__ #pragma diag_suppress 2361 #endif TT { std::declval() } #ifdef __CUDACC__ #pragma diag_default 2361 #endif , std::is_move_assignable()); template static auto test(int, std::false_type) -> std::false_type; template static auto test(...) -> std::false_type; public: static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; }; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // Check for output streamability // Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream template class is_ostreamable { template static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); template static auto test(...) -> std::false_type; public: static constexpr bool value = decltype(test(0))::value; }; /// Check for input streamability template class is_istreamable { template static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); template static auto test(...) -> std::false_type; public: static constexpr bool value = decltype(test(0))::value; }; /// Check for complex template class is_complex { template static auto test(int) -> decltype(std::declval().real(), std::declval().imag(), std::true_type()); template static auto test(...) -> std::false_type; public: static constexpr bool value = decltype(test(0))::value; }; /// Templated operation to get a value from a stream template ::value, detail::enabler> = detail::dummy> bool from_stream(const std::string &istring, T &obj) { std::istringstream is; is.str(istring); is >> obj; return !is.fail() && !is.rdbuf()->in_avail(); } template ::value, detail::enabler> = detail::dummy> bool from_stream(const std::string & /*istring*/, T & /*obj*/) { return false; } // check to see if an object is a mutable container (fail by default) template struct is_mutable_container : std::false_type {}; /// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and /// end methods and an insert function. And for our purposes we exclude std::string and types that can be constructed /// from a std::string template struct is_mutable_container< T, conditional_t().end()), decltype(std::declval().clear()), decltype(std::declval().insert(std::declval().end())>(), std::declval()))>, void>> : public conditional_t::value, std::false_type, std::true_type> {}; // check to see if an object is a mutable container (fail by default) template struct is_readable_container : std::false_type {}; /// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end /// methods and an insert function. And for our purposes we exclude std::string and types that can be constructed from /// a std::string template struct is_readable_container< T, conditional_t().end()), decltype(std::declval().begin())>, void>> : public std::true_type {}; // check to see if an object is a wrapper (fail by default) template struct is_wrapper : std::false_type {}; // check if an object is a wrapper (it has a value_type defined) template struct is_wrapper, void>> : public std::true_type {}; // Check for tuple like types, as in classes with a tuple_size type trait template class is_tuple_like { template // static auto test(int) // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); static auto test(int) -> decltype(std::tuple_size::type>::value, std::true_type{}); template static auto test(...) -> std::false_type; public: static constexpr bool value = decltype(test(0))::value; }; /// Convert an object to a string (directly forward if this can become a string) template ::value, detail::enabler> = detail::dummy> auto to_string(T &&value) -> decltype(std::forward(value)) { return std::forward(value); } /// Construct a string from the object template ::value && !std::is_convertible::value, detail::enabler> = detail::dummy> std::string to_string(const T &value) { return std::string(value); } /// Convert an object to a string (streaming must be supported for that type) template ::value && !std::is_constructible::value && is_ostreamable::value, detail::enabler> = detail::dummy> std::string to_string(T &&value) { std::stringstream stream; stream << value; return stream.str(); } /// If conversion is not supported, return an empty string (streaming is not supported for that type) template ::value && !is_ostreamable::value && !is_readable_container::type>::value, detail::enabler> = detail::dummy> std::string to_string(T &&) { return std::string{}; } /// convert a readable container to a string template ::value && !is_ostreamable::value && is_readable_container::value, detail::enabler> = detail::dummy> std::string to_string(T &&variable) { auto cval = variable.begin(); auto end = variable.end(); if(cval == end) { return std::string("{}"); } std::vector defaults; while(cval != end) { defaults.emplace_back(CLI::detail::to_string(*cval)); ++cval; } return std::string("[" + detail::join(defaults) + "]"); } /// special template overload template ::value, detail::enabler> = detail::dummy> auto checked_to_string(T &&value) -> decltype(to_string(std::forward(value))) { return to_string(std::forward(value)); } /// special template overload template ::value, detail::enabler> = detail::dummy> std::string checked_to_string(T &&) { return std::string{}; } /// get a string as a convertible value for arithmetic types template ::value, detail::enabler> = detail::dummy> std::string value_string(const T &value) { return std::to_string(value); } /// get a string as a convertible value for enumerations template ::value, detail::enabler> = detail::dummy> std::string value_string(const T &value) { return std::to_string(static_cast::type>(value)); } /// for other types just use the regular to_string function template ::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> auto value_string(const T &value) -> decltype(to_string(value)) { return to_string(value); } /// template to get the underlying value type if it exists or use a default template struct wrapped_type { using type = def; }; /// Type size for regular object types that do not look like a tuple template struct wrapped_type::value>::type> { using type = typename T::value_type; }; /// This will only trigger for actual void type template struct type_count_base { static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template struct type_count_base::value && !is_mutable_container::value && !std::is_void::value>::type> { static constexpr int value{1}; }; /// the base tuple size template struct type_count_base::value && !is_mutable_container::value>::type> { static constexpr int value{std::tuple_size::value}; }; /// Type count base for containers is the type_count_base of the individual element template struct type_count_base::value>::type> { static constexpr int value{type_count_base::value}; }; /// Set of overloads to get the type size of an object /// forward declare the subtype_count structure template struct subtype_count; /// forward declare the subtype_count_min structure template struct subtype_count_min; /// This will only trigger for actual void type template struct type_count { static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template struct type_count::value && !is_tuple_like::value && !is_complex::value && !std::is_void::value>::type> { static constexpr int value{1}; }; /// Type size for complex since it sometimes looks like a wrapper template struct type_count::value>::type> { static constexpr int value{2}; }; /// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) template struct type_count::value>::type> { static constexpr int value{subtype_count::value}; }; /// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes) template struct type_count::value && !is_complex::value && !is_tuple_like::value && !is_mutable_container::value>::type> { static constexpr int value{type_count::value}; }; /// 0 if the index > tuple size template constexpr typename std::enable_if::value, int>::type tuple_type_size() { return 0; } /// Recursively generate the tuple type name template constexpr typename std::enable_if < I::value, int>::type tuple_type_size() { return subtype_count::type>::value + tuple_type_size(); } /// Get the type size of the sum of type sizes for all the individual tuple types template struct type_count::value>::type> { static constexpr int value{tuple_type_size()}; }; /// definition of subtype count template struct subtype_count { static constexpr int value{is_mutable_container::value ? expected_max_vector_size : type_count::value}; }; /// This will only trigger for actual void type template struct type_count_min { static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template struct type_count_min< T, typename std::enable_if::value && !is_tuple_like::value && !is_wrapper::value && !is_complex::value && !std::is_void::value>::type> { static constexpr int value{type_count::value}; }; /// Type size for complex since it sometimes looks like a wrapper template struct type_count_min::value>::type> { static constexpr int value{1}; }; /// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) template struct type_count_min< T, typename std::enable_if::value && !is_complex::value && !is_tuple_like::value>::type> { static constexpr int value{subtype_count_min::value}; }; /// 0 if the index > tuple size template constexpr typename std::enable_if::value, int>::type tuple_type_size_min() { return 0; } /// Recursively generate the tuple type name template constexpr typename std::enable_if < I::value, int>::type tuple_type_size_min() { return subtype_count_min::type>::value + tuple_type_size_min(); } /// Get the type size of the sum of type sizes for all the individual tuple types template struct type_count_min::value>::type> { static constexpr int value{tuple_type_size_min()}; }; /// definition of subtype count template struct subtype_count_min { static constexpr int value{is_mutable_container::value ? ((type_count::value < expected_max_vector_size) ? type_count::value : 0) : type_count_min::value}; }; /// This will only trigger for actual void type template struct expected_count { static const int value{0}; }; /// For most types the number of expected items is 1 template struct expected_count::value && !is_wrapper::value && !std::is_void::value>::type> { static constexpr int value{1}; }; /// number of expected items in a vector template struct expected_count::value>::type> { static constexpr int value{expected_max_vector_size}; }; /// number of expected items in a vector template struct expected_count::value && is_wrapper::value>::type> { static constexpr int value{expected_count::value}; }; // Enumeration of the different supported categorizations of objects enum class object_category : int { char_value = 1, integral_value = 2, unsigned_integral = 4, enumeration = 6, boolean_value = 8, floating_point = 10, number_constructible = 12, double_constructible = 14, integer_constructible = 16, // string like types string_assignable = 23, string_constructible = 24, other = 45, // special wrapper or container types wrapper_value = 50, complex_number = 60, tuple_value = 70, container_value = 80, }; /// Set of overloads to classify an object according to type /// some type that is not otherwise recognized template struct classify_object { static constexpr object_category value{object_category::other}; }; /// Signed integers template struct classify_object< T, typename std::enable_if::value && !std::is_same::value && std::is_signed::value && !is_bool::value && !std::is_enum::value>::type> { static constexpr object_category value{object_category::integral_value}; }; /// Unsigned integers template struct classify_object::value && std::is_unsigned::value && !std::is_same::value && !is_bool::value>::type> { static constexpr object_category value{object_category::unsigned_integral}; }; /// single character values template struct classify_object::value && !std::is_enum::value>::type> { static constexpr object_category value{object_category::char_value}; }; /// Boolean values template struct classify_object::value>::type> { static constexpr object_category value{object_category::boolean_value}; }; /// Floats template struct classify_object::value>::type> { static constexpr object_category value{object_category::floating_point}; }; /// String and similar direct assignment template struct classify_object::value && !std::is_integral::value && std::is_assignable::value>::type> { static constexpr object_category value{object_category::string_assignable}; }; /// String and similar constructible and copy assignment template struct classify_object< T, typename std::enable_if::value && !std::is_integral::value && !std::is_assignable::value && (type_count::value == 1) && std::is_constructible::value>::type> { static constexpr object_category value{object_category::string_constructible}; }; /// Enumerations template struct classify_object::value>::type> { static constexpr object_category value{object_category::enumeration}; }; template struct classify_object::value>::type> { static constexpr object_category value{object_category::complex_number}; }; /// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, /// vectors, and enumerations template struct uncommon_type { using type = typename std::conditional::value && !std::is_integral::value && !std::is_assignable::value && !std::is_constructible::value && !is_complex::value && !is_mutable_container::value && !std::is_enum::value, std::true_type, std::false_type>::type; static constexpr bool value = type::value; }; /// wrapper type template struct classify_object::value && is_wrapper::value && !is_tuple_like::value && uncommon_type::value)>::type> { static constexpr object_category value{object_category::wrapper_value}; }; /// Assignable from double or int template struct classify_object::value && type_count::value == 1 && !is_wrapper::value && is_direct_constructible::value && is_direct_constructible::value>::type> { static constexpr object_category value{object_category::number_constructible}; }; /// Assignable from int template struct classify_object::value && type_count::value == 1 && !is_wrapper::value && !is_direct_constructible::value && is_direct_constructible::value>::type> { static constexpr object_category value{object_category::integer_constructible}; }; /// Assignable from double template struct classify_object::value && type_count::value == 1 && !is_wrapper::value && is_direct_constructible::value && !is_direct_constructible::value>::type> { static constexpr object_category value{object_category::double_constructible}; }; /// Tuple type template struct classify_object< T, typename std::enable_if::value && ((type_count::value >= 2 && !is_wrapper::value) || (uncommon_type::value && !is_direct_constructible::value && !is_direct_constructible::value))>::type> { static constexpr object_category value{object_category::tuple_value}; // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be // constructed from just the first element so tuples of can be constructed from a string, which // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2 // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out // those cases that are caught by other object classifications }; /// container type template struct classify_object::value>::type> { static constexpr object_category value{object_category::container_value}; }; // Type name print /// Was going to be based on /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template /// But this is cleaner and works better in this case template ::value == object_category::char_value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "CHAR"; } template ::value == object_category::integral_value || classify_object::value == object_category::integer_constructible, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "INT"; } template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "UINT"; } template ::value == object_category::floating_point || classify_object::value == object_category::number_constructible || classify_object::value == object_category::double_constructible, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "FLOAT"; } /// Print name for enumeration types template ::value == object_category::enumeration, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "ENUM"; } /// Print name for enumeration types template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "BOOLEAN"; } /// Print name for enumeration types template ::value == object_category::complex_number, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "COMPLEX"; } /// Print for all other types template ::value >= object_category::string_assignable && classify_object::value <= object_category::other, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "TEXT"; } /// typename for tuple value template ::value == object_category::tuple_value && type_count_base::value >= 2, detail::enabler> = detail::dummy> std::string type_name(); // forward declaration /// Generate type name for a wrapper or container value template ::value == object_category::container_value || classify_object::value == object_category::wrapper_value, detail::enabler> = detail::dummy> std::string type_name(); // forward declaration /// Print name for single element tuple types template ::value == object_category::tuple_value && type_count_base::value == 1, detail::enabler> = detail::dummy> inline std::string type_name() { return type_name::type>::type>(); } /// Empty string if the index > tuple size template inline typename std::enable_if::value, std::string>::type tuple_name() { return std::string{}; } /// Recursively generate the tuple type name template inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_name() { std::string str = std::string(type_name::type>::type>()) + ',' + tuple_name(); if(str.back() == ',') str.pop_back(); return str; } /// Print type name for tuples with 2 or more elements template ::value == object_category::tuple_value && type_count_base::value >= 2, detail::enabler>> inline std::string type_name() { auto tname = std::string(1, '[') + tuple_name(); tname.push_back(']'); return tname; } /// get the type name for a type that has a value_type member template ::value == object_category::container_value || classify_object::value == object_category::wrapper_value, detail::enabler>> inline std::string type_name() { return type_name(); } // Lexical cast /// Convert to an unsigned integral template ::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(output_ll); if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { return true; } val = nullptr; std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0); if(val == (input.c_str() + input.size())) { output = (output_sll < 0) ? static_cast(0) : static_cast(output_sll); return (static_cast(output) == output_sll); } return false; } /// Convert to a signed integral template ::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(output_ll); if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { return true; } if(input == "true") { // this is to deal with a few oddities with flags and wrapper int types output = static_cast(1); return true; } return false; } /// Convert a flag into an integer value typically binary flags inline std::int64_t to_flag_value(std::string val) { static const std::string trueString("true"); static const std::string falseString("false"); if(val == trueString) { return 1; } if(val == falseString) { return -1; } val = detail::to_lower(val); std::int64_t ret; if(val.size() == 1) { if(val[0] >= '1' && val[0] <= '9') { return (static_cast(val[0]) - '0'); } switch(val[0]) { case '0': case 'f': case 'n': case '-': ret = -1; break; case 't': case 'y': case '+': ret = 1; break; default: throw std::invalid_argument("unrecognized character"); } return ret; } if(val == trueString || val == "on" || val == "yes" || val == "enable") { ret = 1; } else if(val == falseString || val == "off" || val == "no" || val == "disable") { ret = -1; } else { ret = std::stoll(val); } return ret; } /// Integer conversion template ::value == object_category::integral_value || classify_object::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { return integral_conversion(input, output); } /// char values template ::value == object_category::char_value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { if(input.size() == 1) { output = static_cast(input[0]); return true; } return integral_conversion(input, output); } /// Boolean values template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { try { auto out = to_flag_value(input); output = (out > 0); return true; } catch(const std::invalid_argument &) { 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 template ::value == object_category::floating_point, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { if(input.empty()) { return false; } char *val = nullptr; auto output_ld = std::strtold(input.c_str(), &val); output = static_cast(output_ld); return val == (input.c_str() + input.size()); } /// complex template ::value == object_category::complex_number, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { using XC = typename wrapped_type::type; XC x{0.0}, y{0.0}; auto str1 = input; bool worked = false; auto nloc = str1.find_last_of("+-"); if(nloc != std::string::npos && nloc > 0) { worked = detail::lexical_cast(str1.substr(0, nloc), x); str1 = str1.substr(nloc); if(str1.back() == 'i' || str1.back() == 'j') str1.pop_back(); worked = worked && detail::lexical_cast(str1, y); } else { if(str1.back() == 'i' || str1.back() == 'j') { str1.pop_back(); worked = detail::lexical_cast(str1, y); x = XC{0}; } else { worked = detail::lexical_cast(str1, x); y = XC{0}; } } if(worked) { output = T{x, y}; return worked; } return from_stream(input, output); } /// String and similar direct assignment template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { output = input; return true; } /// String and similar constructible and copy assignment template < typename T, enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { output = T(input); return true; } /// Enumerations template ::value == object_category::enumeration, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { typename std::underlying_type::type val; if(!integral_conversion(input, val)) { return false; } output = static_cast(val); return true; } /// wrapper types template ::value == object_category::wrapper_value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { typename T::value_type val; if(lexical_cast(input, val)) { output = val; return true; } return from_stream(input, output); } template ::value == object_category::wrapper_value && !std::is_assignable::value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { typename T::value_type val; if(lexical_cast(input, val)) { output = T{val}; return true; } return from_stream(input, output); } /// Assignable from double or int template < typename T, enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { int val; if(integral_conversion(input, val)) { output = T(val); return true; } else { double dval; if(lexical_cast(input, dval)) { output = T{dval}; return true; } } return from_stream(input, output); } /// Assignable from int template < typename T, enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { int val; if(integral_conversion(input, val)) { output = T(val); return true; } return from_stream(input, output); } /// Assignable from double template < typename T, enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { double val; if(lexical_cast(input, val)) { output = T{val}; return true; } return from_stream(input, output); } /// Non-string convertible from an int template ::value == object_category::other && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { int val; if(integral_conversion(input, val)) { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4800) #endif // with Atomic this could produce a warning due to the conversion but if atomic gets here it is an old style // so will most likely still work output = val; #ifdef _MSC_VER #pragma warning(pop) #endif return true; } // LCOV_EXCL_START // This version of cast is only used for odd cases in an older compilers the fail over // from_stream is tested elsewhere an not relevant for coverage here return from_stream(input, output); // LCOV_EXCL_STOP } /// Non-string parsable by a stream template ::value == object_category::other && !std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { static_assert(is_istreamable::value, "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(...) with XC being the known type"); return from_stream(input, output); } /// Assign a value through lexical cast operations /// Strings can be empty so we need to do a little different template ::value && (classify_object::value == object_category::string_assignable || classify_object::value == object_category::string_constructible), detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { return lexical_cast(input, output); } /// Assign a value through lexical cast operations template ::value && std::is_assignable::value && classify_object::value != object_category::string_assignable && classify_object::value != object_category::string_constructible, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { if(input.empty()) { output = AssignTo{}; return true; } return lexical_cast(input, output); } /// Assign a value through lexical cast operations template ::value && !std::is_assignable::value && classify_object::value == object_category::wrapper_value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { if(input.empty()) { typename AssignTo::value_type emptyVal{}; output = emptyVal; return true; } return lexical_cast(input, output); } /// Assign a value through lexical cast operations for int compatible values /// mainly for atomic operations on some compilers template ::value && !std::is_assignable::value && classify_object::value != object_category::wrapper_value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { if(input.empty()) { output = 0; return true; } int val; if(lexical_cast(input, val)) { output = val; return true; } return false; } /// Assign a value converted from a string in lexical cast to the output value directly template ::value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { ConvertTo val{}; bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; if(parse_result) { output = val; } return parse_result; } /// Assign a value from a lexical cast through constructing a value and move assigning it template < typename AssignTo, typename ConvertTo, enable_if_t::value && !std::is_assignable::value && std::is_move_assignable::value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { ConvertTo val{}; bool parse_result = input.empty() ? true : lexical_cast(input, val); if(parse_result) { output = AssignTo(val); // use () form of constructor to allow some implicit conversions } return parse_result; } /// primary lexical conversion operation, 1 string to 1 type of some kind template ::value <= object_category::other && classify_object::value <= object_category::wrapper_value, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { return lexical_assign(strings[0], output); } /// Lexical conversion if there is only one element but the conversion type is for two, then call a two element /// constructor template ::value <= 2) && expected_count::value == 1 && is_tuple_like::value && type_count_base::value == 2, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { // the remove const is to handle pair types coming from a container typename std::remove_const::type>::type v1; typename std::tuple_element<1, ConvertTo>::type v2; bool retval = lexical_assign(strings[0], v1); if(strings.size() > 1) { retval = retval && lexical_assign(strings[1], v2); } if(retval) { output = AssignTo{v1, v2}; } return retval; } /// Lexical conversion of a container types of single elements template ::value && is_mutable_container::value && type_count::value == 1, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { output.erase(output.begin(), output.end()); if(strings.size() == 1 && strings[0] == "{}") { return true; } bool skip_remaining = false; if(strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) { skip_remaining = true; } for(const auto &elem : strings) { typename AssignTo::value_type out; bool retval = lexical_assign(elem, out); if(!retval) { return false; } output.insert(output.end(), std::move(out)); if(skip_remaining) { break; } } return (!output.empty()); } /// Lexical conversion for complex types template ::value, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { if(strings.size() >= 2 && !strings[1].empty()) { using XC2 = typename wrapped_type::type; XC2 x{0.0}, y{0.0}; auto str1 = strings[1]; if(str1.back() == 'i' || str1.back() == 'j') { str1.pop_back(); } auto worked = detail::lexical_cast(strings[0], x) && detail::lexical_cast(str1, y); if(worked) { output = ConvertTo{x, y}; } return worked; } else { return lexical_assign(strings[0], output); } } /// Conversion to a vector type using a particular single type as the conversion type template ::value && (expected_count::value == 1) && (type_count::value == 1), detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { bool retval = true; output.clear(); output.reserve(strings.size()); for(const auto &elem : strings) { output.emplace_back(); retval = retval && lexical_assign(elem, output.back()); } return (!output.empty()) && retval; } // forward declaration /// Lexical conversion of a container types with conversion type of two elements template ::value && is_mutable_container::value && type_count_base::value == 2, detail::enabler> = detail::dummy> bool lexical_conversion(std::vector strings, AssignTo &output); /// Lexical conversion of a vector types with type_size >2 forward declaration template ::value && is_mutable_container::value && type_count_base::value != 2 && ((type_count::value > 2) || (type_count::value > type_count_base::value)), detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output); /// Conversion for tuples template ::value && is_tuple_like::value && (type_count_base::value != type_count::value || type_count::value > 2), detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output); // forward declaration /// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large /// tuple template ::value && !is_mutable_container::value && classify_object::value != object_category::wrapper_value && (is_mutable_container::value || type_count::value > 2), detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { ConvertTo val; auto retval = lexical_conversion(strings, val); output = AssignTo{val}; return retval; } output = AssignTo{}; return true; } /// function template for converting tuples if the static Index is greater than the tuple size template inline typename std::enable_if<(I >= type_count_base::value), bool>::type tuple_conversion(const std::vector &, AssignTo &) { return true; } /// Conversion of a tuple element where the type size ==1 and not a mutable container template inline typename std::enable_if::value && type_count::value == 1, bool>::type tuple_type_conversion(std::vector &strings, AssignTo &output) { auto retval = lexical_assign(strings[0], output); strings.erase(strings.begin()); return retval; } /// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container template inline typename std::enable_if::value && (type_count::value > 1) && type_count::value == type_count_min::value, bool>::type tuple_type_conversion(std::vector &strings, AssignTo &output) { auto retval = lexical_conversion(strings, output); strings.erase(strings.begin(), strings.begin() + type_count::value); return retval; } /// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes template inline typename std::enable_if::value || type_count::value != type_count_min::value, bool>::type tuple_type_conversion(std::vector &strings, AssignTo &output) { std::size_t index{subtype_count_min::value}; const std::size_t mx_count{subtype_count::value}; const std::size_t mx{(std::max)(mx_count, strings.size())}; while(index < mx) { if(is_separator(strings[index])) { break; } ++index; } bool retval = lexical_conversion( std::vector(strings.begin(), strings.begin() + static_cast(index)), output); strings.erase(strings.begin(), strings.begin() + static_cast(index) + 1); return retval; } /// Tuple conversion operation template inline typename std::enable_if<(I < type_count_base::value), bool>::type tuple_conversion(std::vector strings, AssignTo &output) { bool retval = true; using ConvertToElement = typename std:: conditional::value, typename std::tuple_element::type, ConvertTo>::type; if(!strings.empty()) { retval = retval && tuple_type_conversion::type, ConvertToElement>( strings, std::get(output)); } retval = retval && tuple_conversion(std::move(strings), output); return retval; } /// Lexical conversion of a container types with tuple elements of size 2 template ::value && is_mutable_container::value && type_count_base::value == 2, detail::enabler>> bool lexical_conversion(std::vector strings, AssignTo &output) { output.clear(); while(!strings.empty()) { typename std::remove_const::type>::type v1; typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; bool retval = tuple_type_conversion(strings, v1); if(!strings.empty()) { retval = retval && tuple_type_conversion(strings, v2); } if(retval) { output.insert(output.end(), typename AssignTo::value_type{v1, v2}); } else { return false; } } return (!output.empty()); } /// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2 template ::value && is_tuple_like::value && (type_count_base::value != type_count::value || type_count::value > 2), detail::enabler>> bool lexical_conversion(const std::vector &strings, AssignTo &output) { static_assert( !is_tuple_like::value || type_count_base::value == type_count_base::value, "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); return tuple_conversion(strings, output); } /// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1 template ::value && is_mutable_container::value && type_count_base::value != 2 && ((type_count::value > 2) || (type_count::value > type_count_base::value)), detail::enabler>> bool lexical_conversion(const std::vector &strings, AssignTo &output) { bool retval = true; output.clear(); std::vector temp; std::size_t ii{0}; std::size_t icount{0}; std::size_t xcm{type_count::value}; auto ii_max = strings.size(); while(ii < ii_max) { temp.push_back(strings[ii]); ++ii; ++icount; if(icount == xcm || is_separator(temp.back()) || ii == ii_max) { if(static_cast(xcm) > type_count_min::value && is_separator(temp.back())) { temp.pop_back(); } typename AssignTo::value_type temp_out; retval = retval && lexical_conversion(temp, temp_out); temp.clear(); if(!retval) { return false; } output.insert(output.end(), std::move(temp_out)); icount = 0; } } return retval; } /// conversion for wrapper types template ::value == object_category::wrapper_value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { if(strings.empty() || strings.front().empty()) { output = ConvertTo{}; return true; } typename ConvertTo::value_type val; if(lexical_conversion(strings, val)) { output = ConvertTo{val}; return true; } return false; } /// conversion for wrapper types template ::value == object_category::wrapper_value && !std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_conversion(const std::vector &strings, AssignTo &output) { using ConvertType = typename ConvertTo::value_type; if(strings.empty() || strings.front().empty()) { output = ConvertType{}; return true; } ConvertType val; if(lexical_conversion(strings, val)) { output = val; return true; } return false; } /// Sum a vector of strings inline std::string sum_string_vector(const std::vector &values) { double val{0.0}; bool fail{false}; std::string output; for(const auto &arg : values) { double tv{0.0}; auto comp = detail::lexical_cast(arg, tv); if(!comp) { try { tv = static_cast(detail::to_flag_value(arg)); } catch(const std::exception &) { fail = true; break; } } val += tv; } if(fail) { for(const auto &arg : values) { output.append(arg); } } else { if(val <= static_cast(std::numeric_limits::min()) || val >= static_cast(std::numeric_limits::max()) || val == static_cast(val)) { output = detail::value_string(static_cast(val)); } else { output = detail::value_string(val); } } return output; } } // namespace detail // [CLI11:type_tools_hpp:end] } // namespace CLI