Browse Source

refactor: more cleanup

Sam Jaffe 3 years ago
parent
commit
c287cee13f
3 changed files with 32 additions and 63 deletions
  1. 16 23
      include/string_utils/cast.h
  2. 6 17
      include/string_utils/split.h
  3. 10 23
      test/cast_test.cxx

+ 16 - 23
include/string_utils/cast.h

@@ -24,7 +24,7 @@
 
 namespace string_utils {
 // A helper object for providing partial specializations for casting
-template <typename, typename = void> struct cast_helper {};
+template <typename, typename = void> struct cast_helper;
 
 // The main parser
 template <typename T, typename S> std::pair<T, bool> cast(S const &str) noexcept;
@@ -33,9 +33,10 @@ template <typename S, typename T> bool cast(std::vector<S> const &strs, T & to)
 }
 
 namespace string_utils::detail {
+template <typename> struct always_false : std::false_type {};
 template <typename, typename, typename = void> struct has_cast_helper : std::false_type {};
 template <typename T, typename S>
-struct has_cast_helper<T, S, std::void_t<std::result_of_t<cast_helper<T>(S, T)>>>
+struct has_cast_helper<T, S, std::void_t<decltype(cast_helper<T>{}(S{}, T{}))>>
     : std::true_type {};
 
 template <typename, typename = void> struct is_tuple : std::false_type {};
@@ -68,22 +69,11 @@ template <typename S> std::vector<S> keyval(S const &input);
 namespace string_utils {
 
 template <typename T>
-bool cast(std::string_view str, T & to) noexcept {
-  if constexpr (detail::has_cast_helper<T, std::string>{}) {
-    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<T, std::string>{}, "no cast available");
-  }
-  return true;
-}
+bool cast(std::string_view str, T & to) noexcept { return cast_helper<T>{}(str, to); }
 
 template <typename S, typename T>
 bool cast(std::vector<S> const &strs, T & to) noexcept {
-  if constexpr(detail::has_cast_helper<T, std::vector<S>>{}) {
-    return cast_helper<T>{}(strs, to);
-  } else if constexpr (detail::is_tuple<T>{}) {
+  if constexpr (detail::is_tuple<T>{}) {
     constexpr size_t N = std::tuple_size_v<T>;
     return strs.size() == N && detail::cast_tuple(strs, to, std::make_index_sequence<N>{});
   } else if constexpr (detail::is_associative<T>{}) {
@@ -99,12 +89,12 @@ bool cast(std::vector<S> const &strs, T & to) noexcept {
       to.insert(to.end(), std::move(tmp));
     }
   } else {
-    static_assert(!detail::has_cast_helper<T, std::vector<S>>{}, "no cast available");
+    return cast_helper<T>{}(strs, to);
   }
   return true;
 }
 
-template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
+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;
@@ -112,14 +102,14 @@ template <typename V, typename T> bool maybe_cast(std::string const & str, T & t
 
 template <typename... Ts>
 struct cast_helper<std::variant<Ts...>> {
-  bool operator()(std::string const &str, std::variant<Ts...> & to) const noexcept {
+  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 const &str, std::optional<T> & to) const noexcept {
+  bool operator()(std::string_view str, std::optional<T> & to) const noexcept {
     return maybe_cast<T>(str, to) || true;
   }
 };
@@ -128,6 +118,11 @@ struct cast_helper<std::optional<T>> {
 
 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));
 }
@@ -149,10 +144,9 @@ inline bool cast(std::string_view str, long double & to) noexcept {
 }
 
 inline bool cast(std::string_view str, int & to) noexcept {
-  long tmp;
-  bool rval = cast(str, tmp);
+  auto [tmp, success] = cast<long>(str);
   to = static_cast<int>(tmp);
-  return rval && tmp == static_cast<long>(to);
+  return success && tmp == static_cast<long>(to);
 }
 
 inline bool cast(std::string_view str, bool & to) noexcept {
@@ -192,7 +186,6 @@ template <typename S> std::vector<S> keyval(S const &input) {
 // This should be placed last in the file
 namespace string_utils {
 template <typename T, typename S> std::pair<T, bool> cast(S const & str) noexcept {
-  using string_utils::cast;
   std::pair<detail::decay_t<T>, bool> rval;
   rval.second = cast(str, rval.first);
   return rval;

+ 6 - 17
include/string_utils/split.h

@@ -8,22 +8,11 @@
 
 namespace string_utils {
 
-template <typename... Ts>
-auto to_tuple(std::string const &input, char const f = ',') {
-  std::tuple<Ts...> rval;
-  auto result = tokenizer({f}, {'"', ""})(input);
-  return std::make_pair(rval, cast(result, rval));
-}
-
-template <typename K, typename V = std::string>
-auto to_keyval(std::string const &input, char const f = '=') {
-  std::pair<K, V> rval;
-  auto result = tokenizer({f}).max_outputs(2)(input);
-  return std::make_pair(rval, cast(result, rval));
-}
-
-inline auto to_keyval(std::string const &input, char const f = '=') {
-  return to_keyval<std::string, std::string>(input, f);
-}
+template <typename Tuple>
+struct cast_helper<Tuple, std::enable_if_t<detail::is_tuple<Tuple>{}>> {
+  bool operator()(std::string_view str, Tuple &to) const noexcept {
+    return maybe_cast<detail::decay_t<Tuple>>(tokenizer(",")(std::string(str)), to);
+  }
+};
 
 }

+ 10 - 23
test/cast_test.cxx

@@ -6,8 +6,8 @@
 //  Copyright © 2021 Sam Jaffe. All rights reserved.
 //
 
-#include "string_utils/cast.h"
 #include "string_utils/split.h"
+#include "string_utils/cast.h"
 
 #include "xcode_gtest_helper.h"
 
@@ -88,51 +88,38 @@ TEST(CastVariantTest, EvaluatesTypesLeftToRight) {
 }
 
 TEST(CastKeyValTest, FailsOnTooFewTokens) {
-  auto [value, success] = to_keyval("key");
+  auto [value, success] = cast<std::pair<std::string, std::string>>("key");
   EXPECT_FALSE(success);
 }
 
 TEST(CastKeyValTest, SplitsOnFirstToken) {
-  auto [value, success] = to_keyval("key=value");
+  auto [value, success] = cast<std::pair<std::string, std::string>>("key,value");
   EXPECT_TRUE(success);
   EXPECT_THAT(value, Pair("key", "value"));
 }
 
-TEST(CastKeyValTest, CanProvideAlternateSplitToken) {
-  auto [value, success] = to_keyval("key=value", 'v');
-  EXPECT_TRUE(success);
-  EXPECT_THAT(value, Pair("key=", "alue"));
-}
-
-TEST(CastKeyValTest, ExtraTokensAreStoredInValue) {
-  auto [value, success] = to_keyval("key=value=mapping");
-  EXPECT_TRUE(success);
-  EXPECT_THAT(value, Pair("key", "value=mapping"));
+TEST(CastKeyValTest, FailsOnTooManyTokens) {
+  auto [value, success] = cast<std::pair<std::string, std::string>>("key,value,mapping");
+  EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, FailsOnTooFewTokens) {
-  auto [value, success] = to_tuple<int, int, std::string>("0,A");
+  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,A");
   EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, FailsOnTooManyTokens) {
-  auto [value, success] = to_tuple<int, int, std::string>("0,1,A,B");
+  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,1,A,B");
   EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, ParsesIfAllGood) {
-  auto [value, success] = to_tuple<int, int, std::string>("0,1,A");
+  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,1,A");
   EXPECT_TRUE(success);
   EXPECT_THAT(value, FieldsAre(0, 1, "A"));
 }
 
 TEST(CastTupleTest, FailsOnAnyParseError) {
-  auto [value, success] = to_tuple<int, int, std::string>("0,Q,A");
+  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,Q,A");
   EXPECT_FALSE(success);
 }
-
-TEST(CastTupleTest, CanQuoteArguments) {
-  auto [value, success] = to_tuple<int, int, std::string>("0,1,\"A,B\"");
-  EXPECT_TRUE(success);
-  EXPECT_THAT(value, FieldsAre(0, 1, "A,B"));
-}