Commit e83ef279 by Jake Cobb Committed by Copybara-Service

Add conversion operator to std::array for StrSplit.

This is similar to the conversion operator to std::pair, but for N elements instead of two. Missing elements are filled with the empty string and extra elements are discarded.

PiperOrigin-RevId: 693727220
Change-Id: Icfee16613a4859b019ca043da712f20797386beb
parent 4794821d
...@@ -253,6 +253,10 @@ using ShouldUseLifetimeBoundForPair = std::integral_constant< ...@@ -253,6 +253,10 @@ using ShouldUseLifetimeBoundForPair = std::integral_constant<
(std::is_same<First, absl::string_view>::value || (std::is_same<First, absl::string_view>::value ||
std::is_same<Second, absl::string_view>::value)>; std::is_same<Second, absl::string_view>::value)>;
template <typename StringType, typename ElementType, std::size_t Size>
using ShouldUseLifetimeBoundForArray = std::integral_constant<
bool, std::is_same<StringType, std::string>::value &&
std::is_same<ElementType, absl::string_view>::value>;
// This class implements the range that is returned by absl::StrSplit(). This // This class implements the range that is returned by absl::StrSplit(). This
// class has templated conversion operators that allow it to be implicitly // class has templated conversion operators that allow it to be implicitly
...@@ -344,7 +348,38 @@ class Splitter { ...@@ -344,7 +348,38 @@ class Splitter {
return ConvertToPair<First, Second>(); return ConvertToPair<First, Second>();
} }
// Returns an array with its elements set to the first few strings returned by
// the begin() iterator. If there is not a corresponding value the empty
// string is used.
template <typename ElementType, std::size_t Size,
std::enable_if_t<ShouldUseLifetimeBoundForArray<
StringType, ElementType, Size>::value,
std::nullptr_t> = nullptr>
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::array<ElementType, Size>() const ABSL_ATTRIBUTE_LIFETIME_BOUND {
return ConvertToArray<ElementType, Size>();
}
template <typename ElementType, std::size_t Size,
std::enable_if_t<!ShouldUseLifetimeBoundForArray<
StringType, ElementType, Size>::value,
std::nullptr_t> = nullptr>
// NOLINTNEXTLINE(google-explicit-constructor)
operator std::array<ElementType, Size>() const {
return ConvertToArray<ElementType, Size>();
}
private: private:
template <typename ElementType, std::size_t Size>
std::array<ElementType, Size> ConvertToArray() const {
std::array<ElementType, Size> a;
auto it = begin();
for (std::size_t i = 0; i < Size && it != end(); ++i, ++it) {
a[i] = ElementType(*it);
}
return a;
}
template <typename First, typename Second> template <typename First, typename Second>
std::pair<First, Second> ConvertToPair() const { std::pair<First, Second> ConvertToPair() const {
absl::string_view first, second; absl::string_view first, second;
......
...@@ -444,8 +444,10 @@ using EnableSplitIfString = ...@@ -444,8 +444,10 @@ using EnableSplitIfString =
// behavior works for: // behavior works for:
// //
// 1) All standard STL containers including `std::vector`, `std::list`, // 1) All standard STL containers including `std::vector`, `std::list`,
// `std::deque`, `std::set`,`std::multiset`, 'std::map`, and `std::multimap` // `std::deque`, `std::set`,`std::multiset`, 'std::map`, and `std::multimap`.
// 2) `std::pair` (which is not actually a container). See below. // 2) `std::pair` (which is not actually a container). See below.
// 3) `std::array`, which is a container but has different behavior due to its
// fixed size. See below.
// //
// Example: // Example:
// //
...@@ -487,6 +489,21 @@ using EnableSplitIfString = ...@@ -487,6 +489,21 @@ using EnableSplitIfString =
// std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ','); // std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
// // p.first == "a", p.second == "b" // "c" is omitted. // // p.first == "a", p.second == "b" // "c" is omitted.
// //
//
// Splitting to `std::array` is similar to splitting to `std::pair`, but for
// N elements instead of two; missing elements are filled with the empty string
// and extra elements are discarded.
//
// Examples:
//
// // Stores first two split strings as the elements in a std::array.
// std::array<std::string, 2> a = absl::StrSplit("a,b,c", ',');
// // a[0] == "a", a[1] == "b" // "c" is omitted.
//
// // The second element is empty.
// std::array<std::string, 2> a = absl::StrSplit("a,", ',');
// // a[0] == "a", a[1] == ""
//
// The `StrSplit()` function can be used multiple times to perform more // The `StrSplit()` function can be used multiple times to perform more
// complicated splitting logic, such as intelligently parsing key-value pairs. // complicated splitting logic, such as intelligently parsing key-value pairs.
// //
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "absl/strings/str_split.h" #include "absl/strings/str_split.h"
#include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <deque> #include <deque>
...@@ -397,6 +398,12 @@ void TestPairConversionOperator(const Splitter& splitter) { ...@@ -397,6 +398,12 @@ void TestPairConversionOperator(const Splitter& splitter) {
EXPECT_EQ(p, (std::pair<FirstType, SecondType>("a", "b"))); EXPECT_EQ(p, (std::pair<FirstType, SecondType>("a", "b")));
} }
template <typename StringType, typename Splitter>
void TestArrayConversionOperator(const Splitter& splitter) {
std::array<StringType, 2> a = splitter;
EXPECT_THAT(a, ElementsAre("a", "b"));
}
TEST(Splitter, ConversionOperator) { TEST(Splitter, ConversionOperator) {
auto splitter = absl::StrSplit("a,b,c,d", ','); auto splitter = absl::StrSplit("a,b,c,d", ',');
...@@ -467,6 +474,10 @@ TEST(Splitter, ConversionOperator) { ...@@ -467,6 +474,10 @@ TEST(Splitter, ConversionOperator) {
TestPairConversionOperator<absl::string_view, std::string>(splitter); TestPairConversionOperator<absl::string_view, std::string>(splitter);
TestPairConversionOperator<std::string, absl::string_view>(splitter); TestPairConversionOperator<std::string, absl::string_view>(splitter);
TestPairConversionOperator<std::string, std::string>(splitter); TestPairConversionOperator<std::string, std::string>(splitter);
// Tests conversion to std::array
TestArrayConversionOperator<std::string>(splitter);
TestArrayConversionOperator<absl::string_view>(splitter);
} }
// A few additional tests for conversion to std::pair. This conversion is // A few additional tests for conversion to std::pair. This conversion is
...@@ -511,6 +522,41 @@ TEST(Splitter, ToPair) { ...@@ -511,6 +522,41 @@ TEST(Splitter, ToPair) {
} }
} }
// std::array tests similar to std::pair tests above, testing fewer, exactly,
// or more elements than the array size.
TEST(Splitter, ToArray) {
{
// Empty string
std::array<std::string, 2> p = absl::StrSplit("", ',');
EXPECT_THAT(p, ElementsAre("", ""));
}
{
// Only first
std::array<std::string, 2> p = absl::StrSplit("a", ',');
EXPECT_THAT(p, ElementsAre("a", ""));
}
{
// Only second
std::array<std::string, 2> p = absl::StrSplit(",b", ',');
EXPECT_THAT(p, ElementsAre("", "b"));
}
{
// First and second.
std::array<std::string, 2> p = absl::StrSplit("a,b", ',');
EXPECT_THAT(p, ElementsAre("a", "b"));
}
{
// First and second and then more stuff that will be ignored.
std::array<std::string, 2> p = absl::StrSplit("a,b,c", ',');
EXPECT_THAT(p, ElementsAre("a", "b"));
// "c" is omitted.
}
}
TEST(Splitter, Predicates) { TEST(Splitter, Predicates) {
static const char kTestChars[] = ",a, ,b,"; static const char kTestChars[] = ",a, ,b,";
using absl::AllowEmpty; using absl::AllowEmpty;
......
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