Commit 9f95e046 authored by Marcus Holland-Moritz's avatar Marcus Holland-Moritz Committed by Dave Watson

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<unsigned int>(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
parent 86fd9acf
...@@ -347,25 +347,36 @@ template<class String> StringPiece prepareDelim(const String& s) { ...@@ -347,25 +347,36 @@ template<class String> StringPiece prepareDelim(const String& s) {
} }
inline char prepareDelim(char c) { return c; } inline char prepareDelim(char c) { return c; }
template <class Dst>
struct convertTo {
template <class Src>
static Dst from(const Src& src) { return folly::to<Dst>(src); }
static Dst from(const Dst& src) { return src; }
};
template<bool exact, template<bool exact,
class Delim> class Delim,
bool splitFixed(const Delim& delimiter, class OutputType>
StringPiece input, typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
StringPiece& out) { splitFixed(const Delim& delimiter,
StringPiece input,
OutputType& out) {
if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) { if (exact && UNLIKELY(std::string::npos != input.find(delimiter))) {
return false; return false;
} }
out = input; out = convertTo<OutputType>::from(input);
return true; return true;
} }
template<bool exact, template<bool exact,
class Delim, class Delim,
class... StringPieces> class OutputType,
bool splitFixed(const Delim& delimiter, class... OutputTypes>
StringPiece input, typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
StringPiece& outHead, splitFixed(const Delim& delimiter,
StringPieces&... outTail) { StringPiece input,
OutputType& outHead,
OutputTypes&... outTail) {
size_t cut = input.find(delimiter); size_t cut = input.find(delimiter);
if (UNLIKELY(cut == std::string::npos)) { if (UNLIKELY(cut == std::string::npos)) {
return false; return false;
...@@ -374,7 +385,7 @@ bool splitFixed(const Delim& delimiter, ...@@ -374,7 +385,7 @@ bool splitFixed(const Delim& delimiter,
StringPiece tail(input.begin() + cut + detail::delimSize(delimiter), StringPiece tail(input.begin() + cut + detail::delimSize(delimiter),
input.end()); input.end());
if (LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) { if (LIKELY(splitFixed<exact>(delimiter, tail, outTail...))) {
outHead = head; outHead = convertTo<OutputType>::from(head);
return true; return true;
} }
return false; return false;
...@@ -423,11 +434,13 @@ void splitTo(const Delim& delimiter, ...@@ -423,11 +434,13 @@ void splitTo(const Delim& delimiter,
template<bool exact, template<bool exact,
class Delim, class Delim,
class... StringPieces> class OutputType,
bool split(const Delim& delimiter, class... OutputTypes>
StringPiece input, typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
StringPiece& outHead, split(const Delim& delimiter,
StringPieces&... outTail) { StringPiece input,
OutputType& outHead,
OutputTypes&... outTail) {
return detail::splitFixed<exact>( return detail::splitFixed<exact>(
detail::prepareDelim(delimiter), detail::prepareDelim(delimiter),
input, input,
......
...@@ -384,16 +384,24 @@ void splitTo(const Delim& delimiter, ...@@ -384,16 +384,24 @@ void splitTo(const Delim& delimiter,
bool ignoreEmpty = false); bool ignoreEmpty = false);
/* /*
* Split a string into a fixed number of pieces by delimiter. Returns 'true' if * Split a string into a fixed number of string pieces and/or numeric types
* the fields were all successfully populated. * 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; * folly::StringPiece name, key, value;
* if (folly::split('\t', line, 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 * 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 * 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 * the input string does not exactly match the number of output parameters
...@@ -403,14 +411,24 @@ void splitTo(const Delim& delimiter, ...@@ -403,14 +411,24 @@ void splitTo(const Delim& delimiter,
* folly::StringPiece x, y. * folly::StringPiece x, y.
* if (folly::split<false>(':', "a:b:c", x, y)) * if (folly::split<false>(':', "a:b:c", x, y))
* assert(x == "a" && y == "b:c"); * 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 <class T>
using IsSplitTargetType = std::integral_constant<bool,
std::is_arithmetic<T>::value ||
std::is_same<T, StringPiece>::value>;
template<bool exact = true, template<bool exact = true,
class Delim, class Delim,
class... StringPieces> class OutputType,
bool split(const Delim& delimiter, class... OutputTypes>
StringPiece input, typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
StringPiece& outHead, split(const Delim& delimiter,
StringPieces&... outTail); StringPiece input,
OutputType& outHead,
OutputTypes&... outTail);
/* /*
* Join list of tokens. * Join list of tokens.
......
...@@ -834,6 +834,34 @@ TEST(Split, fixed) { ...@@ -834,6 +834,34 @@ TEST(Split, fixed) {
EXPECT_FALSE(folly::split('.', "a.b", a)); 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<false>(':', "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<false>(':', "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<false>(':', "a:13:14.7:b", a, b, c),
std::range_error);
}
TEST(String, join) { TEST(String, join) {
string output; string output;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment