Skip to content

Commit

Permalink
Support numeric types as targets for folly::split
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Marcus Holland-Moritz authored and Dave Watson committed Mar 10, 2014
1 parent 86fd9ac commit 9f95e04
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 25 deletions.
45 changes: 29 additions & 16 deletions folly/String-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
36 changes: 27 additions & 9 deletions folly/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
28 changes: 28 additions & 0 deletions folly/test/StringTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 9f95e04

Please sign in to comment.