// // cast.h // string-utils // // Created by Sam Jaffe on 2/13/21. // Copyright © 2021 Sam Jaffe. All rights reserved. // #pragma once #include #include #include #include #include #if __has_include() && __cplusplus > 201402L #define STRING_UTIL_CAST_STD_VARIANT #include #endif #include "any_of.h" #define CAST_NUMBER_IMPL(type, func, ...) \ inline bool cast(std::string const & str, type & to) { \ char * rval; \ to = func(str.c_str(), &rval, ##__VA_ARGS__); \ return rval == str.c_str() + str.length(); \ } namespace string_utils { namespace traits { template struct is_stringy : std::false_type {}; #if defined(STRING_UTIL_CAST_STD_VARIANT) template struct is_variant : std::false_type {}; template struct is_variant> : std::true_type {}; template struct is_stringy< T, std::enable_if_t{} && !is_variant{}>> : std::true_type {}; #else template struct is_stringy< T, std::enable_if_t{}>> : std::true_type {}; #endif }} namespace string_utils { template std::pair cast(std::string const & str); template bool cast(std::string const & str, std::optional & to); #if defined(STRING_UTIL_CAST_STD_VARIANT) template bool cast(std::string const & str, std::variant & to); #endif } namespace string_utils { template {}>> bool cast(std::string const & str, T & to) { to = T(str); return true; } CAST_NUMBER_IMPL(long, std::strtol, 10); CAST_NUMBER_IMPL(long long, std::strtoll, 10); CAST_NUMBER_IMPL(float, std::strtof); CAST_NUMBER_IMPL(double, std::strtod); CAST_NUMBER_IMPL(long double, std::strtold); inline bool cast(std::string const & str, int & to) { long tmp; bool rval = cast(str, tmp); to = static_cast(tmp); return rval && tmp == static_cast(to); } inline bool cast(std::string const & str, bool & to) { if (any_of(str, "true", "TRUE", "YES", "1")) { to = true; return true; } else if (any_of(str, "false", "FALSE", "NO", "0")) { to = false; return true; } return false; } } #if defined(STRING_UTIL_CAST_STD_VARIANT) namespace string_utils::detail { template bool _cast(std::string const & str, std::variant & to) { auto [rval, found] = cast>>(str); if (found) { to = std::move(rval); } return found; } // This is needed due to a compiler bug in Clang that fails to properly compile // fold-expressions. template bool _cast_chain(std::string const & str, std::variant & to) { if constexpr (I == sizeof...(Ts)) { return false; } else { return _cast(str, to) || _cast_chain(str, to); } } } namespace string_utils { template bool cast(std::string const & str, std::variant & to) { return detail::_cast_chain<0>(str, to); } } #endif namespace string_utils { template bool cast(std::string const & str, std::optional & to) { auto [value, success] = cast(str); if (success) { to = std::move(value); } return true; } } // This should be placed last in the file namespace string_utils { template std::pair cast(std::string const & str) { using string_utils::cast; std::pair rval; rval.second = cast(str, rval.first); return rval; } } #undef CAST_NUMBER_IMPL