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) {
}
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,
class Delim>
bool splitFixed(const Delim& delimiter,
StringPiece input,
StringPiece& out) {
class Delim,
class OutputType>
typename std::enable_if<IsSplitTargetType<OutputType>::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<OutputType>::from(input);
return true;
}
template<bool exact,
class Delim,
class... StringPieces>
bool splitFixed(const Delim& delimiter,
StringPiece input,
StringPiece& outHead,
StringPieces&... outTail) {
class OutputType,
class... OutputTypes>
typename std::enable_if<IsSplitTargetType<OutputType>::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<exact>(delimiter, tail, outTail...))) {
outHead = head;
outHead = convertTo<OutputType>::from(head);
return true;
}
return false;
......@@ -423,11 +434,13 @@ void splitTo(const Delim& delimiter,
template<bool exact,
class Delim,
class... StringPieces>
bool split(const Delim& delimiter,
StringPiece input,
StringPiece& outHead,
StringPieces&... outTail) {
class OutputType,
class... OutputTypes>
typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
split(const Delim& delimiter,
StringPiece input,
OutputType& outHead,
OutputTypes&... outTail) {
return detail::splitFixed<exact>(
detail::prepareDelim(delimiter),
input,
......
......@@ -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<false>(':', "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 <class T>
using IsSplitTargetType = std::integral_constant<bool,
std::is_arithmetic<T>::value ||
std::is_same<T, StringPiece>::value>;
template<bool exact = true,
class Delim,
class... StringPieces>
bool split(const Delim& delimiter,
StringPiece input,
StringPiece& outHead,
StringPieces&... outTail);
class OutputType,
class... OutputTypes>
typename std::enable_if<IsSplitTargetType<OutputType>::value, bool>::type
split(const Delim& delimiter,
StringPiece input,
OutputType& outHead,
OutputTypes&... outTail);
/*
* Join list of tokens.
......
......@@ -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<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) {
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