|
|
@@ -13,34 +13,63 @@
|
|
|
#include <type_traits>
|
|
|
#include <utility>
|
|
|
|
|
|
+#if __has_include(<variant>) && __cplusplus > 201402L
|
|
|
+#define STRING_UTIL_CAST_STD_VARIANT
|
|
|
+#include <variant>
|
|
|
+#endif
|
|
|
+
|
|
|
#include "any_of.h"
|
|
|
|
|
|
-#define cast_number_impl(type, func, ...) \
|
|
|
-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(); \
|
|
|
-}
|
|
|
+#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 <typename T, typename = void> struct is_stringy : std::false_type {};
|
|
|
+#if defined(STRING_UTIL_CAST_STD_VARIANT)
|
|
|
+
|
|
|
+template <typename> struct is_variant : std::false_type {};
|
|
|
+template <typename... Ts>
|
|
|
+struct is_variant<std::variant<Ts...>> : std::true_type {};
|
|
|
+
|
|
|
+template <typename T>
|
|
|
+struct is_stringy<
|
|
|
+ T, std::enable_if_t<std::is_constructible<T, std::string const &>{} &&
|
|
|
+ !is_variant<T>{}>> : std::true_type {};
|
|
|
+#else
|
|
|
+template <typename T>
|
|
|
+struct is_stringy<
|
|
|
+ T, std::enable_if_t<std::is_constructible<T, std::string const &>{}>>
|
|
|
+ : std::true_type {};
|
|
|
+#endif
|
|
|
+}}
|
|
|
|
|
|
namespace string_utils {
|
|
|
+template <typename T> std::pair<T, bool> cast(std::string const & str);
|
|
|
|
|
|
-template <typename T, typename = std::enable_if_t<std::is_constructible<T, std::string const &>{}>>
|
|
|
-bool cast(std::string const &str, std::string &to) { to = T(str); return true; }
|
|
|
+template <typename T, typename = std::enable_if_t<traits::is_stringy<T>{}>>
|
|
|
+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)
|
|
|
+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);
|
|
|
|
|
|
-bool cast(std::string const &str, int &to) {
|
|
|
+inline bool cast(std::string const & str, int & to) {
|
|
|
long tmp;
|
|
|
bool rval = cast(str, tmp);
|
|
|
to = static_cast<int>(tmp);
|
|
|
return rval && tmp == static_cast<long>(to);
|
|
|
}
|
|
|
|
|
|
-bool cast(std::string const &str, bool &to) {
|
|
|
+inline bool cast(std::string const & str, bool & to) {
|
|
|
if (any_of(str, "true", "TRUE", "YES", "1")) {
|
|
|
to = true;
|
|
|
return true;
|
|
|
@@ -50,13 +79,45 @@ bool cast(std::string const &str, bool &to) {
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
-template <typename T>
|
|
|
-auto cast(std::string const &str) {
|
|
|
+#if defined(STRING_UTIL_CAST_STD_VARIANT)
|
|
|
+namespace string_utils::detail {
|
|
|
+template <size_t I, typename... Ts>
|
|
|
+bool _cast(std::string const & str, std::variant<Ts...> & to) {
|
|
|
+ auto [rval, found] = cast<std::tuple_element_t<I, std::tuple<Ts...>>>(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 <size_t I, typename... Ts>
|
|
|
+bool _cast_chain(std::string const & str, std::variant<Ts...> & to) {
|
|
|
+ if constexpr (I == sizeof...(Ts)) {
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ return _cast<I>(str, to) || _cast_chain<I + 1>(str, to);
|
|
|
+ }
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+namespace string_utils {
|
|
|
+template <typename... Ts>
|
|
|
+bool cast(std::string const & str, std::variant<Ts...> & to) {
|
|
|
+ return detail::_cast_chain<0>(str, to);
|
|
|
+}
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+// This should be placed last in the file
|
|
|
+namespace string_utils {
|
|
|
+template <typename T> std::pair<T, bool> cast(std::string const & str) {
|
|
|
using string_utils::cast;
|
|
|
std::pair<T, bool> rval;
|
|
|
rval.second = cast(str, rval.first);
|
|
|
return rval;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
+
|
|
|
+#undef CAST_NUMBER_IMPL
|