Skip to content

Commit

Permalink
Add a string_view overload to absl::StrJoin
Browse files Browse the repository at this point in the history
This allows users to pass a collection of string-like objects in an
`initializer_list` without having to convert everything to the same
type first.

`initializer_list` has the requirement that everything is copied in to
the list. For strings hitting the `string_view` overload avoids
unnecessary copies.

This may be a breaking change if `absl::StrJoin` has an explicit
template parameter, for example `absl::StrJoin<std::string>({foo,
"bar"});`. In this case, remove the explicit template parameter.

PiperOrigin-RevId: 633295575
Change-Id: Ie5f0860f409f639a27a58949842ec961e0b3bdeb
  • Loading branch information
derekmauro authored and copybara-github committed May 13, 2024
1 parent 692d9e5 commit 7384185
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 4 deletions.
21 changes: 17 additions & 4 deletions absl/strings/str_join.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,20 @@ std::string StrJoin(const Range& range, absl::string_view separator,
return strings_internal::JoinRange(range, separator, fmt);
}

template <typename T, typename Formatter>
template <typename T, typename Formatter,
typename = typename std::enable_if<
!std::is_convertible<T, absl::string_view>::value>::type>
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator,
Formatter&& fmt) {
return strings_internal::JoinRange(il, separator, fmt);
}

template <typename Formatter>
inline std::string StrJoin(std::initializer_list<absl::string_view> il,
absl::string_view separator, Formatter&& fmt) {
return strings_internal::JoinRange(il, separator, fmt);
}

template <typename... T, typename Formatter>
std::string StrJoin(const std::tuple<T...>& value, absl::string_view separator,
Formatter&& fmt) {
Expand All @@ -269,9 +277,14 @@ std::string StrJoin(const Range& range, absl::string_view separator) {
return strings_internal::JoinRange(range, separator);
}

template <typename T>
std::string StrJoin(std::initializer_list<T> il,
absl::string_view separator) {
template <typename T, typename = typename std::enable_if<!std::is_convertible<
T, absl::string_view>::value>::type>
std::string StrJoin(std::initializer_list<T> il, absl::string_view separator) {
return strings_internal::JoinRange(il, separator);
}

inline std::string StrJoin(std::initializer_list<absl::string_view> il,
absl::string_view separator) {
return strings_internal::JoinRange(il, separator);
}

Expand Down
36 changes: 36 additions & 0 deletions absl/strings/str_join_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,42 @@ TEST(StrJoin, InitializerList) {
}
}

TEST(StrJoin, StringViewInitializerList) {
{
// Tests initializer_list of string_views
std::string b = "b";
EXPECT_EQ("a-b-c", absl::StrJoin({"a", b, "c"}, "-"));
}
{
// Tests initializer_list of string_views with a non-default formatter
TestingParenFormatter f;
std::string b = "b";
EXPECT_EQ("(a)-(b)-(c)", absl::StrJoin({"a", b, "c"}, "-", f));
}

class NoCopy {
public:
explicit NoCopy(absl::string_view view) : view_(view) {}
NoCopy(const NoCopy&) = delete;
operator absl::string_view() { return view_; } // NOLINT
private:
absl::string_view view_;
};
{
// Tests initializer_list of string_views preferred over initializer_list<T>
// for T that is implicitly convertible to string_view
EXPECT_EQ("a-b-c",
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-"));
}
{
// Tests initializer_list of string_views preferred over initializer_list<T>
// for T that is implicitly convertible to string_view
TestingParenFormatter f;
EXPECT_EQ("(a)-(b)-(c)",
absl::StrJoin({NoCopy("a"), NoCopy("b"), NoCopy("c")}, "-", f));
}
}

TEST(StrJoin, Tuple) {
EXPECT_EQ("", absl::StrJoin(std::make_tuple(), "-"));
EXPECT_EQ("hello", absl::StrJoin(std::make_tuple("hello"), "-"));
Expand Down

0 comments on commit 7384185

Please sign in to comment.