ソースを参照

Modify cast utils to support ADL

Sam Jaffe 2 年 前
コミット
fa2351eb2b
3 ファイル変更109 行追加130 行削除
  1. 82 117
      include/string_utils/cast.h
  2. 4 6
      include/string_utils/forwards.h
  3. 23 7
      include/string_utils/traits.h

+ 82 - 117
include/string_utils/cast.h

@@ -21,156 +21,121 @@
 #include "string_utils/forwards.h"
 #include "string_utils/traits.h"
 
-#define SAFE_NUMBER_PARSE(func, ...) \
-  [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
+#define SAFE_NUMBER_PARSE(func, ...)                                           \
+  [](char const * in, char ** out) { return func(in, out, ##__VA_ARGS__); }
 
 namespace string_utils::detail {
-template <typename T, typename F>
-bool cast_number(std::string_view str, T & to, F func) noexcept;
-template <typename S> std::vector<S> keyval(S const &input);
-}
-
-namespace string_utils {
-
-inline bool cast(std::string_view str, std::string & to) noexcept {
-  to = std::string(str);
-  return true;
-}
-
-inline bool cast(std::string_view str, long & to) noexcept {
-  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
-}
-
-inline bool cast(std::string_view str, long long & to) noexcept {
-  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
-}
-
-inline bool cast(std::string_view str, float & to) noexcept {
-  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
-}
-
-inline bool cast(std::string_view str, double & to) noexcept {
-  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
+template <typename Actual, typename Out>
+bool ctor_cast(Out & out, std::string_view str) noexcept {
+  auto [rval, found] = cast<Actual>(str);
+  if (found) { out = Out(std::move(rval)); }
+  return found;
 }
 
-inline bool cast(std::string_view str, long double & to) noexcept {
-  return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
+template <typename Tuple, size_t... Is>
+bool cast_tuple(std::vector<std::string_view> const & str, Tuple & to,
+                std::index_sequence<Is...>) noexcept {
+  return ((cast(std::get<Is>(to), str[Is])) && ...);
 }
 
-inline bool cast(std::string_view str, int & to) noexcept {
-  auto [tmp, success] = cast<long>(str);
-  to = static_cast<int>(tmp);
-  return success && tmp == static_cast<long>(to);
+template <typename T, typename F>
+bool cast_number(std::string_view str, T & to, F func) noexcept {
+  char * counter = nullptr;
+  to = func(str.data(), &counter);
+  return counter == str.end();
 }
 
-inline bool cast(std::string_view str, bool & to) noexcept {
-  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;
+std::vector<std::string_view> keyval(std::string_view input) {
+  if (size_t const pos = input.find('='); pos < input.size()) {
+    return {input.substr(0, pos), input.substr(pos + 1)};
   }
-  return false;
+  return {input};
 }
-
 }
 
+// This should be placed last in the file
 namespace string_utils {
 
-template <typename V, typename S, typename T> bool maybe_cast(S const & str, T & to) noexcept {
-  auto [rval, found] = cast<V>(str);
-  if (found) { to = std::move(rval); }
-  return found;
-}
-
-template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_container<T>{}>> {
-  template <typename S>
-  bool operator()(std::vector<S> const &strs, T & to) const noexcept {
-    for (S const &elem : strs) {
-      if constexpr (detail::is_associative<T>{}) {
-        auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
-        if (!success) { return false; }
-        to.insert(std::move(tmp));
-      } else {
-        auto [tmp, success] = cast<typename T::value_type>(elem);
-        if (!success) { return false; }
-        to.insert(to.end(), std::move(tmp));
-      }
+template <typename Out> bool cast(Out & out, std::string_view str) noexcept {
+  if constexpr (std::is_same_v<Out, long>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtol, 10));
+  } else if constexpr (std::is_same_v<Out, long>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtol, 10));
+  } else if constexpr (std::is_same_v<Out, long long>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoll, 10));
+  } else if constexpr (std::is_same_v<Out, float>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtof));
+  } else if constexpr (std::is_same_v<Out, double>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtod));
+  } else if constexpr (std::is_same_v<Out, long double>) {
+    return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtold));
+  } else if constexpr (std::is_same_v<Out, int>) {
+    auto [tmp, success] = cast<long>(str);
+    out = static_cast<int>(tmp);
+    return success && tmp == static_cast<long>(out);
+  } else if constexpr (std::is_same_v<Out, bool>) {
+    if (any_of(str, "true", "TRUE", "YES", "1")) {
+      out = true;
+      return true;
+    } else if (any_of(str, "false", "FALSE", "NO", "0")) {
+      out = false;
+      return true;
     }
+    return false;
+  } else if constexpr (std::is_constructible_v<Out, std::string_view>) {
+    out = Out(str);
     return true;
+  } else {
+    static_assert(detail::always_false<Out>{}, "No match for cast(string)");
   }
-};
-
-template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_tuple<T>{}>> {
-  template <typename S, size_t... Is>
-  bool cast_tuple(S const & str, T & to, std::index_sequence<Is...>) const noexcept {
-    return ((cast(str[Is], std::get<Is>(to))) && ...);
-  }
-  
-  template <typename S> bool operator()(std::vector<S> const &strs, T & to) const noexcept {
-    constexpr size_t N = std::tuple_size_v<T>;
-    return strs.size() == N && cast_tuple(strs, to, std::make_index_sequence<N>{});
-  }
-};
-
-template <typename T>
-struct cast_helper<T, std::enable_if_t<std::is_constructible_v<T, std::string_view>>> {
-  bool operator()(std::string_view str, T & to) const noexcept {
-    to = str;
-    return true;
-  }
-};
-
-template <typename... Ts>
-struct cast_helper<std::variant<Ts...>> {
-  bool operator()(std::string_view 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_view str, std::optional<T> & to) const noexcept {
-    return maybe_cast<T>(str, to) || true;
-  }
-};
-
 }
 
-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...>) noexcept {
-  return ((cast(str[Is], std::get<Is>(to))) && ...);
+template <typename... Ts>
+bool cast(std::variant<Ts...> & out, std::string_view str) noexcept {
+  return (detail::ctor_cast<Ts>(out, str) || ...);
 }
 
-template <typename T, typename F>
-bool cast_number(std::string_view str, T & to, F func) noexcept {
-  char *counter = nullptr;
-  to = func(str.data(), &counter);
-  return counter == str.end();
+template <typename Out>
+bool cast(std::optional<Out> & out, std::string_view str) noexcept {
+  return detail::ctor_cast<Out>(out, str) || true;
 }
 
-template <typename S> std::vector<S> keyval(S const &input) {
-  size_t const pos = input.find('=');
-  return pos == S::npos ? std::vector{input}
-                        : std::vector{input.substr(0, pos), input.substr(pos + 1)};
-}
+template <typename Out>
+bool cast(Out & out, std::vector<std::string_view> const & strs) noexcept {
+  if constexpr (detail::is_associative_v<Out>) {
+    for (auto elem : strs) {
+      auto [tmp, success] =
+          cast<typename Out::value_type>(detail::keyval(elem));
+      if (!success) { return false; }
+      out.insert(std::move(tmp));
+    }
+  } else if constexpr (detail::is_container_v<Out>) {
+    for (auto elem : strs) {
+      auto [tmp, success] = cast<typename Out::value_type>(elem);
+      if (!success) { return false; }
+      out.insert(out.end(), std::move(tmp));
+    }
+  } else if constexpr (detail::is_tuple_v<Out>) {
+    constexpr size_t N = std::tuple_size_v<Out>;
+    return strs.size() == N &&
+           detail::cast_tuple(strs, out, std::make_index_sequence<N>{});
+  } else {
+    static_assert(detail::always_false<Out>{},
+                  "No match for cast(vector<string>)");
+  }
 }
 
-// This should be placed last in the file
-namespace string_utils {
 template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
   std::pair<detail::decay_t<T>, bool> rval;
-  rval.second = cast(str, rval.first);
+  rval.second = cast(rval.first, str);
   return rval;
 }
 
 template <typename T, typename S>
 std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
+  std::vector<std::string_view> tmp{strs.begin(), strs.end()};
   std::pair<detail::decay_t<T>, bool> rval;
-  rval.second = cast(strs, rval.first);
+  rval.second = cast(rval.first, tmp);
   return rval;
 }
 }

+ 4 - 6
include/string_utils/forwards.h

@@ -23,10 +23,8 @@ template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept;
 template <typename T, typename S>
 std::pair<T, bool> cast(std::vector<S> const & str) noexcept;
 
-template <
-    typename S, typename T,
-    typename = std::enable_if_t<detail::has_result<cast_helper<T>(S, T &)>{}>>
-bool cast(S const & str, T & to) noexcept {
-  return cast_helper<T>{}(str, to);
-}
+template <typename Out> bool cast(Out & out, std::string_view str) noexcept;
+
+template <typename Out>
+bool cast(Out & out, std::vector<std::string_view> const & strs) noexcept;
 }

+ 23 - 7
include/string_utils/traits.h

@@ -12,28 +12,44 @@
 #include <type_traits>
 
 namespace string_utils::detail {
-template <typename> struct always_false : std::false_type {};
 template <typename, typename = void> struct has_result : std::false_type {};
 template <typename F>
 struct has_result<F, std::void_t<std::result_of_t<F>>> : std::true_type {};
 
 template <typename, typename = void> struct is_tuple : std::false_type {};
 template <typename T>
-struct is_tuple<T, std::void_t<typename std::tuple_size<T>::type>> : std::true_type {};
+struct is_tuple<T, std::void_t<typename std::tuple_size<T>::type>>
+    : std::true_type {};
 
 template <typename, typename = void> struct is_associative : std::false_type {};
 template <typename T>
-struct is_associative<T, std::void_t<typename T::mapped_type>> : std::true_type {};
+struct is_associative<T, std::void_t<typename T::mapped_type>>
+    : std::true_type {};
 
 template <typename C>
-using insert_t = decltype(std::declval<C>().insert(std::declval<typename C::iterator>(),
-                                                   std::declval<typename C::value_type>()));
+using insert_t =
+    decltype(std::declval<C>().insert(std::declval<typename C::iterator>(),
+                                      std::declval<typename C::value_type>()));
 template <typename, typename = void> struct is_container : std::false_type {};
 template <typename T>
-struct is_container<T, std::void_t<typename T::value_type, insert_t<T>>> : std::true_type {};
+struct is_container<T, std::void_t<typename T::value_type, insert_t<T>>>
+    : std::true_type {};
 
 template <typename T> struct decay { using type = std::decay_t<T>; };
 template <template <typename...> class C, typename... Ts>
-struct decay<C<Ts...>> { using type = C<std::decay_t<Ts>...>; };
+struct decay<C<Ts...>> {
+  using type = C<std::decay_t<Ts>...>;
+};
+}
+
+namespace string_utils::detail {
+template <typename> struct always_false : std::false_type {};
+
+template <typename T> constexpr bool has_result_v = has_result<T>::value;
+template <typename T> constexpr bool is_tuple_v = is_tuple<T>::value;
+template <typename T>
+constexpr bool is_associative_v = is_associative<T>::value;
+template <typename T> constexpr bool is_container_v = is_container<T>::value;
+
 template <typename T> using decay_t = typename decay<std::decay_t<T>>::type;
 }