Skip to content

Commit

Permalink
Add conversion operator to std::array for StrSplit.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jakecobb authored and copybara-github committed Nov 6, 2024
1 parent 4794821 commit e83ef27
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 1 deletion.
35 changes: 35 additions & 0 deletions absl/strings/internal/str_split_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ using ShouldUseLifetimeBoundForPair = std::integral_constant<
(std::is_same<First, 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
// class has templated conversion operators that allow it to be implicitly
Expand Down Expand Up @@ -344,7 +348,38 @@ class Splitter {
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:
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>
std::pair<First, Second> ConvertToPair() const {
absl::string_view first, second;
Expand Down
19 changes: 18 additions & 1 deletion absl/strings/str_split.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,10 @@ using EnableSplitIfString =
// behavior works for:
//
// 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.
// 3) `std::array`, which is a container but has different behavior due to its
// fixed size. See below.
//
// Example:
//
Expand Down Expand Up @@ -487,6 +489,21 @@ using EnableSplitIfString =
// std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
// // 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
// complicated splitting logic, such as intelligently parsing key-value pairs.
//
Expand Down
46 changes: 46 additions & 0 deletions absl/strings/str_split_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "absl/strings/str_split.h"

#include <array>
#include <cstddef>
#include <cstdint>
#include <deque>
Expand Down Expand Up @@ -397,6 +398,12 @@ void TestPairConversionOperator(const Splitter& splitter) {
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) {
auto splitter = absl::StrSplit("a,b,c,d", ',');

Expand Down Expand Up @@ -467,6 +474,10 @@ TEST(Splitter, ConversionOperator) {
TestPairConversionOperator<absl::string_view, std::string>(splitter);
TestPairConversionOperator<std::string, absl::string_view>(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
Expand Down Expand Up @@ -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) {
static const char kTestChars[] = ",a, ,b,";
using absl::AllowEmpty;
Expand Down

0 comments on commit e83ef27

Please sign in to comment.