Bläddra i källkod

refactor: various

Sam Jaffe 3 år sedan
förälder
incheckning
af9a99dbd3
1 ändrade filer med 99 tillägg och 64 borttagningar
  1. 99 64
      include/string_utils/cast.h

+ 99 - 64
include/string_utils/cast.h

@@ -18,60 +18,106 @@
 
 #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();                                 \
-  }
+#define SAFE_NUMBER_PARSE(func, ...) \
+  [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
+
+namespace string_utils {
+// A helper object for providing partial specializations for casting
+template <typename, typename = void> struct cast_helper {};
 
-namespace string_utils { namespace traits {
-template <typename T, typename = void> struct is_stringy : std::false_type {};
+// The main parser
+template <typename T> std::pair<T, bool> cast(std::string const & str) noexcept;
+template <typename T> bool cast(std::string const & str, T & to) noexcept;
 
-template <typename> struct is_variant : std::false_type {};
+// A section of multi-argument parsers
 template <typename... Ts>
-struct is_variant<std::variant<Ts...>> : std::true_type {};
+bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept;
+template <typename K, typename V>
+bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept;
+}
 
+namespace string_utils::detail {
+template <typename, typename = void> struct has_cast_helper : std::false_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 {};
+struct has_cast_helper<T, std::void_t<std::result_of_t<cast_helper<T>(std::string, T)>>>
+    : std::true_type {};
 
-}}
+template <typename T> constexpr bool has_cast_helper_v = has_cast_helper<T>{};
+}
+
+namespace string_utils::detail {
+template <typename Tuple>
+bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept;
+template <typename T, typename F>
+bool cast_number(std::string const &str, T & to, F func) noexcept;
+}
 
 namespace string_utils {
-template <typename T> std::pair<T, bool> cast(std::string const & str);
-template <typename T> bool cast(std::string const & str, std::optional<T> & to);
-template <typename... Ts>
-bool cast(std::string const & str, std::variant<Ts...> & to);
+
+template <typename T>
+bool cast(std::string const & str, T & to) noexcept {
+  if constexpr (detail::has_cast_helper_v<T>) {
+    return cast_helper<T>{}(str, to);
+  } else if constexpr (std::is_same_v<T, std::string>) {
+    to = T(str);
+  } else {
+    static_assert(!detail::has_cast_helper_v<T>, "no cast available");
+  }
+  return true;
+}
+
+template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
+  auto [rval, found] = cast<V>(str);
+  if (found) { to = std::move(rval); }
+  return found;
+}
 
 template <typename... Ts>
-bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to);
-template <typename K, typename V>
-bool cast(std::vector<std::string> const & str, std::pair<K, V> & to);
+struct cast_helper<std::variant<Ts...>> {
+  bool operator()(std::string const &str, std::variant<Ts...> & to) const noexcept {
+    return (maybe_cast<Ts>(str, to) || ...);
+  }
+};
+
+template <typename T>
+struct cast_helper<std::optional<T>> {
+  bool operator()(std::string const &str, std::optional<T> & to) const noexcept {
+    return maybe_cast<T>(str, to) || true;
+  }
+};
+
 }
 
 namespace string_utils {
-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;
+
+inline bool cast(std::string const &str, long & to) noexcept {
+  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
+}
+
+inline bool cast(std::string const &str, long long & to) noexcept {
+  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
+}
+
+inline bool cast(std::string const &str, float & to) noexcept {
+  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
+}
+
+inline bool cast(std::string const &str, double & to) noexcept {
+  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
 }
 
-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, long double & to) noexcept {
+  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
+}
 
-inline bool cast(std::string const & str, int & to) {
+inline bool cast(std::string const & str, int & to) noexcept {
   long tmp;
   bool rval = cast(str, tmp);
   to = static_cast<int>(tmp);
   return rval && tmp == static_cast<long>(to);
 }
 
-inline bool cast(std::string const & str, bool & to) {
+inline bool cast(std::string const & str, bool & to) noexcept {
   if (any_of(str, "true", "TRUE", "YES", "1")) {
     to = true;
     return true;
@@ -81,16 +127,24 @@ inline bool cast(std::string const & str, bool & to) {
   }
   return false;
 }
+
+}
+
+namespace string_utils {
+
+template <typename... Ts>
+bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept {
+  return detail::cast_tuple(str, to);
+}
+
+template <typename K, typename V>
+bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept {
+  return detail::cast_tuple(str, to);
 }
 
-namespace string_utils::detail {
-template <typename T, typename... Ts>
-bool cast_alternative(std::string const & str, std::variant<Ts...> & to) {
-  auto [rval, found] = cast<T>(str);
-  if (found) { to = std::move(rval); }
-  return found;
 }
 
+namespace string_utils::detail {
 template <typename Tuple, size_t... Is>
 bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
                 std::index_sequence<Is...>) {
@@ -98,41 +152,22 @@ bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
 }
 
 template <typename Tuple>
-bool cast_tuple(std::vector<std::string> const & str, Tuple & to) {
+bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept {
   constexpr size_t N = std::tuple_size_v<Tuple>;
   return str.size() == N && cast_tuple(str, to, std::make_index_sequence<N>{});
 }
-}
-
-namespace string_utils {
 
-template <typename... Ts>
-bool cast(std::string const & str, std::variant<Ts...> & to) {
-  return (detail::cast_alternative<Ts>(str, to) || ...);
+template <typename T, typename F>
+bool cast_number(std::string const &str, T & to, F func) noexcept {
+  char *counter = nullptr;
+  to = func(str.c_str(), &counter);
+  return counter == str.c_str() + str.length();
 }
-
-template <typename T>
-bool cast(std::string const & str, std::optional<T> & to) {
-  auto [value, success] = cast<T>(str);
-  if (success) { to = std::move(value); }
-  return true;
-}
-
-template <typename... Ts>
-bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) {
-  return detail::cast_tuple(str, to);
-}
-
-template <typename K, typename V>
-bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) {
-  return detail::cast_tuple(str, to);
-}
-
 }
 
 // This should be placed last in the file
 namespace string_utils {
-template <typename T> std::pair<T, bool> cast(std::string const & str) {
+template <typename T> std::pair<T, bool> cast(std::string const & str) noexcept {
   using string_utils::cast;
   std::pair<T, bool> rval;
   rval.second = cast(str, rval.first);