From 9f95e046d5efc40ca366cf2423e09d13fb8590e3 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Mon, 10 Mar 2014 11:35:00 -0700 Subject: [PATCH] Support numeric types as targets for folly::split Summary: This extends the fixed version of folly::split to support numeric types as targets in addition to string pieces. I was almost certain this was already possible when I recently reviewed some code that did folly::StringPiece source, target, strFrequency; int frequency; if (folly::split('\t', line, source, target, strFrequency) && (frequency = folly::to(strFrequency)) > 0) and was about to suggest changing the above into: folly::StringPiece source, target; int frequency; if (folly::split('\t', line, source, target, frequency) && frequency > 0) I double checked and saw that only splitting to string pieces was supported. In the meantime I came across this pattern again and decided to just make it work because it's really convenient. The implementation should be fully backwards compatible. Test Plan: - New unit tests - fbconfig -r folly && fbmake runtests - Applied to github release, ./configure && make check Reviewed By: andrei.alexandrescu@fb.com FB internal diff: D1187004 --- folly/String-inl.h | 45 +++++++++++++++++++++++++-------------- folly/String.h | 36 +++++++++++++++++++++++-------- folly/test/StringTest.cpp | 28 ++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/folly/String-inl.h b/folly/String-inl.h index a2179209..e680473b 100644 --- a/folly/String-inl.h +++ b/folly/String-inl.h @@ -347,25 +347,36 @@ template StringPiece prepareDelim(const String& s) { } inline char prepareDelim(char c) { return c; } +template +struct convertTo { + template + static Dst from(const Src& src) { return folly::to(src); } + static Dst from(const Dst& src) { return src; } +}; + template -bool splitFixed(const Delim& delimiter, - StringPiece input, - StringPiece& out) { + class Delim, + class OutputType> +typename std::enable_if::value, bool>::type +splitFixed(const Delim& delimiter, + StringPiece input, + OutputType& out) { if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) { return false; } - out = input; + out = convertTo::from(input); return true; } template -bool splitFixed(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail) { + class OutputType, + class... OutputTypes> +typename std::enable_if::value, bool>::type +splitFixed(const Delim& delimiter, + StringPiece input, + OutputType& outHead, + OutputTypes&... outTail) { size_t cut = input.find(delimiter); if (UNLIKELY(cut == std::string::npos)) { return false; @@ -374,7 +385,7 @@ bool splitFixed(const Delim& delimiter, StringPiece tail(input.begin() + cut + detail::delimSize(delimiter), input.end()); if (LIKELY(splitFixed(delimiter, tail, outTail...))) { - outHead = head; + outHead = convertTo::from(head); return true; } return false; @@ -423,11 +434,13 @@ void splitTo(const Delim& delimiter, template -bool split(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail) { + class OutputType, + class... OutputTypes> +typename std::enable_if::value, bool>::type +split(const Delim& delimiter, + StringPiece input, + OutputType& outHead, + OutputTypes&... outTail) { return detail::splitFixed( detail::prepareDelim(delimiter), input, diff --git a/folly/String.h b/folly/String.h index 241d4dbf..b5dc2417 100644 --- a/folly/String.h +++ b/folly/String.h @@ -384,16 +384,24 @@ void splitTo(const Delim& delimiter, bool ignoreEmpty = false); /* - * Split a string into a fixed number of pieces by delimiter. Returns 'true' if - * the fields were all successfully populated. + * Split a string into a fixed number of string pieces and/or numeric types + * by delimiter. Any numeric type that folly::to<> can convert to from a + * string piece is supported as a target. Returns 'true' if the fields were + * all successfully populated. * - * Example: + * Examples: * * folly::StringPiece name, key, value; * if (folly::split('\t', line, name, key, value)) * ... * - * The 'exact' template paremeter specifies how the function behaves when too + * folly::StringPiece name; + * double value; + * int id; + * if (folly::split('\t', line, name, value, id)) + * ... + * + * The 'exact' template parameter specifies how the function behaves when too * many fields are present in the input string. When 'exact' is set to its * default value of 'true', a call to split will fail if the number of fields in * the input string does not exactly match the number of output parameters @@ -403,14 +411,24 @@ void splitTo(const Delim& delimiter, * folly::StringPiece x, y. * if (folly::split(':', "a:b:c", x, y)) * assert(x == "a" && y == "b:c"); + * + * Note that this will likely not work if the last field's target is of numeric + * type, in which case folly::to<> will throw an exception. */ +template +using IsSplitTargetType = std::integral_constant::value || + std::is_same::value>; + template -bool split(const Delim& delimiter, - StringPiece input, - StringPiece& outHead, - StringPieces&... outTail); + class OutputType, + class... OutputTypes> +typename std::enable_if::value, bool>::type +split(const Delim& delimiter, + StringPiece input, + OutputType& outHead, + OutputTypes&... outTail); /* * Join list of tokens. diff --git a/folly/test/StringTest.cpp b/folly/test/StringTest.cpp index 925af1fe..cb3bcb7a 100644 --- a/folly/test/StringTest.cpp +++ b/folly/test/StringTest.cpp @@ -834,6 +834,34 @@ TEST(Split, fixed) { EXPECT_FALSE(folly::split('.', "a.b", a)); } +TEST(Split, fixed_convert) { + StringPiece a, d; + int b; + double c; + + EXPECT_TRUE(folly::split(':', "a:13:14.7:b", a, b, c, d)); + EXPECT_EQ("a", a); + EXPECT_EQ(13, b); + EXPECT_NEAR(14.7, c, 1e-10); + EXPECT_EQ("b", d); + + EXPECT_TRUE(folly::split(':', "b:14:15.3:c", a, b, c, d)); + EXPECT_EQ("b", a); + EXPECT_EQ(14, b); + EXPECT_NEAR(15.3, c, 1e-10); + EXPECT_EQ("c", d); + + EXPECT_FALSE(folly::split(':', "a:13:14.7:b", a, b, d)); + + EXPECT_TRUE(folly::split(':', "a:13:14.7:b", a, b, d)); + EXPECT_EQ("a", a); + EXPECT_EQ(13, b); + EXPECT_EQ("14.7:b", d); + + EXPECT_THROW(folly::split(':', "a:13:14.7:b", a, b, c), + std::range_error); +} + TEST(String, join) { string output; -- 2.34.1