Procházet zdrojové kódy

refactor: improve handling

Sam Jaffe před 3 roky
rodič
revize
94f96e6fa1

+ 64 - 90
include/string_utils/cast.h

@@ -17,50 +17,14 @@
 #include <variant>
 #include <vector>
 
-#include "any_of.h"
+#include "string_utils/any_of.h"
+#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__); }
 
-namespace string_utils {
-// A helper object for providing partial specializations for casting
-template <typename, typename = void> struct cast_helper;
-
-// The main parser
-template <typename T, typename S> std::pair<T, bool> cast(S const &str) noexcept;
-template <typename T> bool cast(std::string_view str, T & to) noexcept;
-template <typename S, typename T> bool cast(std::vector<S> const &strs, T & to) noexcept;
-}
-
 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<decltype(cast_helper<T>{}(S{}, T{}))>>
-    : 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 {};
-
-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 {};
-
-template <typename, typename = void> struct is_container : std::false_type {};
-template <typename T>
-struct is_container<T, std::void_t<typename T::value_type>> : 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>...>; };
-template <typename T> using decay_t = typename decay<std::decay_t<T>>::type;
-}
-
-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;
 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);
@@ -68,56 +32,6 @@ 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 { return cast_helper<T>{}(str, to); }
-
-template <typename S, typename T>
-bool cast(std::vector<S> const &strs, T & to) noexcept {
-  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>{}) {
-    for (S const &elem : strs) {
-      auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
-      if (!success) { return false; }
-      to.insert(std::move(tmp));
-    }
-  } else if constexpr (detail::is_container<T>{}) {
-    for (S const &elem : strs) {
-      auto [tmp, success] = cast<typename T::value_type>(elem);
-      if (!success) { return false; }
-      to.insert(to.end(), std::move(tmp));
-    }
-  } else {
-    return cast_helper<T>{}(strs, to);
-  }
-  return true;
-}
-
-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... 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 {
-
 inline bool cast(std::string_view str, std::string & to) noexcept {
   to = std::string(str);
   return true;
@@ -162,6 +76,60 @@ inline bool cast(std::string_view str, bool & to) noexcept {
 
 }
 
+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));
+      }
+    }
+    return true;
+  }
+};
+
+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... 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,
@@ -185,12 +153,18 @@ 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 {
+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);
   return rval;
 }
 
+template <typename T, typename S>
+std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
+  std::pair<detail::decay_t<T>, bool> rval;
+  rval.second = cast(strs, rval.first);
+  return rval;
+}
 }
 
 #undef CAST_NUMBER_IMPL

+ 24 - 0
include/string_utils/forwards.h

@@ -0,0 +1,24 @@
+//
+//  forwards.h
+//  string-utils
+//
+//  Created by Sam Jaffe on 6/12/22.
+//  Copyright © 2022 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include "string_utils/traits.h"
+
+namespace string_utils {
+// A helper object for providing partial specializations for casting
+template <typename, typename = void> struct cast_helper;
+
+// The main parser
+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); }
+}

+ 0 - 18
include/string_utils/split.h

@@ -1,18 +0,0 @@
-#pragma once
-
-#include <tuple>
-#include <utility>
-
-#include "string_utils/cast.h"
-#include "string_utils/tokenizer.h"
-
-namespace string_utils {
-
-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);
-  }
-};
-
-}

+ 36 - 0
include/string_utils/traits.h

@@ -0,0 +1,36 @@
+//
+//  traits.h
+//  string-utils
+//
+//  Created by Sam Jaffe on 6/12/22.
+//  Copyright © 2022 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <tuple>
+#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 {};
+
+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 {};
+
+template <typename, typename = void> struct is_container : std::false_type {};
+template <typename T>
+struct is_container<T, std::void_t<typename T::value_type>> : 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>...>; };
+template <typename T> using decay_t = typename decay<std::decay_t<T>>::type;
+}

+ 6 - 4
string-utils.xcodeproj/project.pbxproj

@@ -8,6 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		CD11D62125D96C620088CB79 /* cast_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD11D62025D96C620088CB79 /* cast_test.cxx */; };
+		CD25F29E28568BCA006AE2BB /* traits.h in Headers */ = {isa = PBXBuildFile; fileRef = CD25F29D28568BCA006AE2BB /* traits.h */; };
 		CD26686B252FF4E800B3E667 /* string_utils in Headers */ = {isa = PBXBuildFile; fileRef = CD26686A252FF4E100B3E667 /* string_utils */; settings = {ATTRIBUTES = (Public, ); }; };
 		CD26687E252FF62F00B3E667 /* tokenizer.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD26687C252FF62F00B3E667 /* tokenizer.cxx */; };
 		CD26688B252FFAAE00B3E667 /* libstring-utils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD266862252FF4B600B3E667 /* libstring-utils.a */; };
@@ -16,7 +17,6 @@
 		CDC883EA28560A7C0088C91E /* any_of.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC883E228560A7C0088C91E /* any_of.h */; };
 		CDC883EB28560A7C0088C91E /* tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC883E328560A7C0088C91E /* tokenizer.h */; };
 		CDC883EC28560A7C0088C91E /* cast.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC883E428560A7C0088C91E /* cast.h */; };
-		CDC883ED28560A7C0088C91E /* split.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC883E528560A7C0088C91E /* split.h */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -60,6 +60,8 @@
 /* Begin PBXFileReference section */
 		CD11D62025D96C620088CB79 /* cast_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cast_test.cxx; sourceTree = "<group>"; };
 		CD11D62825D96C990088CB79 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
+		CD25F29D28568BCA006AE2BB /* traits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = traits.h; sourceTree = "<group>"; };
+		CD25F2A328568CB6006AE2BB /* forwards.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = forwards.h; sourceTree = "<group>"; };
 		CD266862252FF4B600B3E667 /* libstring-utils.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libstring-utils.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD26686A252FF4E100B3E667 /* string_utils */ = {isa = PBXFileReference; lastKnownFileType = folder; name = string_utils; path = include/string_utils; sourceTree = "<group>"; };
 		CD26686D252FF51F00B3E667 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
@@ -70,7 +72,6 @@
 		CDC883E228560A7C0088C91E /* any_of.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = any_of.h; sourceTree = "<group>"; };
 		CDC883E328560A7C0088C91E /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = "<group>"; };
 		CDC883E428560A7C0088C91E /* cast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cast.h; sourceTree = "<group>"; };
-		CDC883E528560A7C0088C91E /* split.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = split.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -172,9 +173,10 @@
 			isa = PBXGroup;
 			children = (
 				CDC883E228560A7C0088C91E /* any_of.h */,
+				CD25F29D28568BCA006AE2BB /* traits.h */,
 				CDC883E328560A7C0088C91E /* tokenizer.h */,
 				CDC883E428560A7C0088C91E /* cast.h */,
-				CDC883E528560A7C0088C91E /* split.h */,
+				CD25F2A328568CB6006AE2BB /* forwards.h */,
 			);
 			path = string_utils;
 			sourceTree = "<group>";
@@ -187,9 +189,9 @@
 			buildActionMask = 2147483647;
 			files = (
 				CD26686B252FF4E800B3E667 /* string_utils in Headers */,
+				CD25F29E28568BCA006AE2BB /* traits.h in Headers */,
 				CDC883EB28560A7C0088C91E /* tokenizer.h in Headers */,
 				CDC883EA28560A7C0088C91E /* any_of.h in Headers */,
-				CDC883ED28560A7C0088C91E /* split.h in Headers */,
 				CDC883EC28560A7C0088C91E /* cast.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 11 - 9
test/cast_test.cxx

@@ -6,7 +6,6 @@
 //  Copyright © 2021 Sam Jaffe. All rights reserved.
 //
 
-#include "string_utils/split.h"
 #include "string_utils/cast.h"
 
 #include "xcode_gtest_helper.h"
@@ -87,39 +86,42 @@ TEST(CastVariantTest, EvaluatesTypesLeftToRight) {
   EXPECT_THAT(value, HoldsAlternative<int>());
 }
 
+using pair_t = std::pair<std::string, std::string>;
+using tuple_t = std::tuple<int, int, std::string>;
+
 TEST(CastKeyValTest, FailsOnTooFewTokens) {
-  auto [value, success] = cast<std::pair<std::string, std::string>>("key");
+  auto [value, success] = cast<pair_t>(std::vector{"key"});
   EXPECT_FALSE(success);
 }
 
-TEST(CastKeyValTest, SplitsOnFirstToken) {
-  auto [value, success] = cast<std::pair<std::string, std::string>>("key,value");
+TEST(CastKeyValTest, ParsesTokens) {
+  auto [value, success] = cast<pair_t>(std::vector{"key","value"});
   EXPECT_TRUE(success);
   EXPECT_THAT(value, Pair("key", "value"));
 }
 
 TEST(CastKeyValTest, FailsOnTooManyTokens) {
-  auto [value, success] = cast<std::pair<std::string, std::string>>("key,value,mapping");
+  auto [value, success] = cast<pair_t>(std::vector{"key","value","mapping"});
   EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, FailsOnTooFewTokens) {
-  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,A");
+  auto [value, success] = cast<tuple_t>(std::vector{"0","A"});
   EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, FailsOnTooManyTokens) {
-  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,1,A,B");
+  auto [value, success] = cast<tuple_t>(std::vector{"0","1","A","B"});
   EXPECT_FALSE(success);
 }
 
 TEST(CastTupleTest, ParsesIfAllGood) {
-  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,1,A");
+  auto [value, success] = cast<tuple_t>(std::vector{"0","1","A"});
   EXPECT_TRUE(success);
   EXPECT_THAT(value, FieldsAre(0, 1, "A"));
 }
 
 TEST(CastTupleTest, FailsOnAnyParseError) {
-  auto [value, success] = cast<std::tuple<int, int, std::string>>("0,Q,A");
+  auto [value, success] = cast<tuple_t>(std::vector{"0","Q","A"});
   EXPECT_FALSE(success);
 }