#pragma once // Distributed under the 3-Clause BSD License. See accompanying // file LICENSE or https://github.com/CLIUtils/CLI11 for details. #include #include #include #include #include namespace CLI { // 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/master/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; /// Check to see if something is a vector (fail check by default) template struct is_vector : std::false_type {}; /// Check to see if something is a vector (true if actually a vector) template struct is_vector> : std::true_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 copyable pointer template struct is_copyable_ptr { static bool const value = is_shared_ptr::value || std::is_pointer::value; }; /// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that /// pointer_traits be valid. template struct element_type { using type = typename std::conditional::value, typename std::pointer_traits::element_type, T>::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; }; /// 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 { // 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 && std::is_signed::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "INT"; } template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "UINT"; } template ::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "FLOAT"; } /// This one should not be used, since vector types print the internal type template ::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "VECTOR"; } template ::value && !std::is_integral::value && !is_vector::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "TEXT"; } // Lexical cast /// Signed integers / enums template ::value && std::is_signed::value && !is_bool::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { try { size_t n = 0; long long output_ll = std::stoll(input, &n, 0); output = static_cast(output_ll); return n == input.size() && static_cast(output) == output_ll; } catch(const std::invalid_argument &) { return false; } catch(const std::out_of_range &) { return false; } } /// Unsigned integers template ::value && std::is_unsigned::value && !is_bool::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { if(!input.empty() && input.front() == '-') return false; // std::stoull happily converts negative values to junk without any errors. try { size_t n = 0; unsigned long long output_ll = std::stoull(input, &n, 0); output = static_cast(output_ll); return n == input.size() && static_cast(output) == output_ll; } catch(const std::invalid_argument &) { return false; } catch(const std::out_of_range &) { return false; } } /// boolean values template ::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { try { auto out = to_flag_value(input); if(out == "1") { output = true; } else if(out == "-1") { output = false; } else { output = (std::stoll(out) > 0); } return true; } catch(const std::invalid_argument &) { return false; } } /// Floats template ::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { try { size_t n = 0; output = static_cast(std::stold(input, &n)); return n == input.size(); } catch(const std::invalid_argument &) { return false; } catch(const std::out_of_range &) { return false; } } /// String and similar template ::value && !std::is_integral::value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { output = input; return true; } /// Non-string parsable template ::value && !std::is_integral::value && !std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { std::istringstream is; is.str(input); is >> output; return !is.fail() && !is.rdbuf()->in_avail(); } /// sum a vector of flag representations /// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is by /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most /// common true and false strings then uses stoll to convert the rest for summing template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector &flags, T &output) { int64_t count{0}; static const auto trueString = std::string("1"); static const auto falseString = std::string("-1"); for(auto &flag : flags) { count += (flag == trueString) ? 1 : ((flag == falseString) ? (-1) : std::stoll(flag)); } output = (count > 0) ? static_cast(count) : T{0}; } /// sum a vector of flag representations /// The flag vector produces a series of strings in a vector, simple true is represented by a "1", simple false is by /// "-1" an if numbers are passed by some fashion they are captured as well so the function just checks for the most /// common true and false strings then uses stoll to convert the rest for summing template ::value && std::is_signed::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector &flags, T &output) { int64_t count{0}; static const auto trueString = std::string("1"); static const auto falseString = std::string("-1"); for(auto &flag : flags) { count += (flag == trueString) ? 1 : ((flag == falseString) ? (-1) : std::stoll(flag)); } output = static_cast(count); } } // namespace detail } // namespace CLI