#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 namespace CLI { // Type tools /// 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 { static const bool value = false; }; /// Check to see if something is a vector (true if actually a vector) template struct is_vector> { static bool const value = true; }; /// Check to see if something is bool (fail check by default) template struct is_bool { static const bool value = false; }; /// Check to see if something is bool (true if actually a bool) template <> struct is_bool { static bool const value = true; }; 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 = {}; // 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), 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, 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; } } /// 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(); } } // namespace detail } // namespace CLI