<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">On Tue, Sep 27, 2016 at 9:46 AM Zachary Turner via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: zturner<br class="gmail_msg"><br>Date: Tue Sep 27 11:37:30 2016<br class="gmail_msg"><br>New Revision: 282502<br class="gmail_msg"><br><br class="gmail_msg"><br>URL: <a href="http://llvm.org/viewvc/llvm-project?rev=282502&view=rev" rel="noreferrer" class="gmail_msg" target="_blank">http://llvm.org/viewvc/llvm-project?rev=282502&view=rev</a><br class="gmail_msg"><br>Log:<br class="gmail_msg"><br>Add llvm::join_items to StringExtras.<br class="gmail_msg"><br><br class="gmail_msg"><br>llvm::join_items is similar to llvm::join, which produces a string<br class="gmail_msg"><br>by concatenating a sequence of values together separated by a<br class="gmail_msg"><br>given separator.  But it differs in that the arguments to<br class="gmail_msg"><br>llvm::join() are same-type members of a container, whereas the<br class="gmail_msg"><br>arguments to llvm::join_items are arbitrary types passed into<br class="gmail_msg"><br>a variadic template.  The only requirement on parameters to<br class="gmail_msg"><br>llvm::join_items (including for the separator themselves) is<br class="gmail_msg"><br>that they be implicitly convertible to std::string or have<br class="gmail_msg"><br>an overload of std::string::operator+<br class="gmail_msg"><br><br class="gmail_msg"><br>Differential Revision: <a href="https://reviews.llvm.org/D24880" rel="noreferrer" class="gmail_msg" target="_blank">https://reviews.llvm.org/D24880</a><br class="gmail_msg"><br><br class="gmail_msg"><br>Added:<br class="gmail_msg"><br>    llvm/trunk/unittests/ADT/StringExtrasTest.cpp<br class="gmail_msg"><br>Modified:<br class="gmail_msg"><br>    llvm/trunk/include/llvm/ADT/StringExtras.h<br class="gmail_msg"><br>    llvm/trunk/unittests/ADT/CMakeLists.txt<br class="gmail_msg"><br><br class="gmail_msg"><br>Modified: llvm/trunk/include/llvm/ADT/StringExtras.h<br class="gmail_msg"><br>URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/StringExtras.h?rev=282502&r1=282501&r2=282502&view=diff" rel="noreferrer" class="gmail_msg" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/StringExtras.h?rev=282502&r1=282501&r2=282502&view=diff</a><br class="gmail_msg"><br>==============================================================================<br class="gmail_msg"><br>--- llvm/trunk/include/llvm/ADT/StringExtras.h (original)<br class="gmail_msg"><br>+++ llvm/trunk/include/llvm/ADT/StringExtras.h Tue Sep 27 11:37:30 2016<br class="gmail_msg"><br>@@ -155,6 +155,8 @@ static inline StringRef getOrdinalSuffix<br class="gmail_msg"><br> /// it if it is not printable or if it is an escape char.<br class="gmail_msg"><br> void PrintEscapedString(StringRef Name, raw_ostream &Out);<br class="gmail_msg"><br><br class="gmail_msg"><br>+namespace detail {<br class="gmail_msg"><br>+<br class="gmail_msg"><br> template <typename IteratorT><br class="gmail_msg"><br> inline std::string join_impl(IteratorT Begin, IteratorT End,<br class="gmail_msg"><br>                              StringRef Separator, std::input_iterator_tag) {<br class="gmail_msg"><br>@@ -189,12 +191,64 @@ inline std::string join_impl(IteratorT B<br class="gmail_msg"><br>   return S;<br class="gmail_msg"><br> }<br class="gmail_msg"><br><br class="gmail_msg"><br>+template <typename Sep><br class="gmail_msg"><br>+inline void join_items_impl(std::string &Result, Sep Separator) {}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+template <typename Sep, typename Arg><br class="gmail_msg"><br>+inline void join_items_impl(std::string &Result, Sep Separator,<br class="gmail_msg"><br>+                            const Arg &Item) {<br class="gmail_msg"><br>+  Result += Item;<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+template <typename Sep, typename Arg1, typename... Args><br class="gmail_msg"><br>+inline void join_items_impl(std::string &Result, Sep Separator, const Arg1 &A1,<br class="gmail_msg"><br>+                            Args &&... Items) {<br class="gmail_msg"><br>+  Result += A1;<br class="gmail_msg"><br>+  Result += Separator;<br class="gmail_msg"><br>+  join_items_impl(Result, Separator, std::forward<Args>(Items)...);<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+inline size_t join_one_item_size(char C) { return 1; }<br class="gmail_msg"><br>+inline size_t join_one_item_size(const char *S) { return S ? ::strlen(S) : 0; }<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+template <typename T> inline size_t join_one_item_size(const T &Str) {<br class="gmail_msg"><br>+  return Str.size();<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+inline size_t join_items_size() { return 0; }<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+template <typename A1> inline size_t join_items_size(const A1 &A) {<br class="gmail_msg"><br>+  return join_one_item_size(A);<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+template <typename A1, typename... Args><br class="gmail_msg"><br>+inline size_t join_items_size(const A1 &A, Args &&... Items) {<br class="gmail_msg"><br>+  return join_one_item_size(A) + join_items_size(std::forward<Args>(Items)...);<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br> /// Joins the strings in the range [Begin, End), adding Separator between<br class="gmail_msg"><br> /// the elements.<br class="gmail_msg"><br> template <typename IteratorT><br class="gmail_msg"><br> inline std::string join(IteratorT Begin, IteratorT End, StringRef Separator) {<br class="gmail_msg"><br>   typedef typename std::iterator_traits<IteratorT>::iterator_category tag;<br class="gmail_msg"><br>-  return join_impl(Begin, End, Separator, tag());<br class="gmail_msg"><br>+  return detail::join_impl(Begin, End, Separator, tag());<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+/// Joins the strings in the parameter pack \p Items, adding \p Separator<br class="gmail_msg"><br>+/// between the elements.  All arguments must be implicitly convertible to<br class="gmail_msg"><br>+/// std::string, or there should be an overload of std::string::operator+=()<br class="gmail_msg"><br>+/// that accepts the argument explicitly.<br class="gmail_msg"><br>+template <typename Sep, typename... Args><br class="gmail_msg"><br>+inline std::string join_items(Sep Separator, Args &&... Items) {<br class="gmail_msg"><br>+  std::string Result;<br class="gmail_msg"><br>+  if (sizeof...(Items) == 0)<br class="gmail_msg"><br>+    return Result;<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  size_t NS = detail::join_one_item_size(Separator);<br class="gmail_msg"><br>+  size_t NI = detail::join_items_size(std::forward<Args>(Items)...);<br class="gmail_msg"><br>+  Result.reserve(NI + (sizeof...(Items) - 1) * NS + 1);<br class="gmail_msg"><br>+  detail::join_items_impl(Result, Separator, std::forward<Args>(Items)...);<br class="gmail_msg"><br>+  return Result;<br class="gmail_msg"><br> }<br class="gmail_msg"><br><br class="gmail_msg"><br> } // End llvm namespace<br class="gmail_msg"><br><br class="gmail_msg"><br>Modified: llvm/trunk/unittests/ADT/CMakeLists.txt<br class="gmail_msg"><br>URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/CMakeLists.txt?rev=282502&r1=282501&r2=282502&view=diff" rel="noreferrer" class="gmail_msg" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/CMakeLists.txt?rev=282502&r1=282501&r2=282502&view=diff</a><br class="gmail_msg"><br>==============================================================================<br class="gmail_msg"><br>--- llvm/trunk/unittests/ADT/CMakeLists.txt (original)<br class="gmail_msg"><br>+++ llvm/trunk/unittests/ADT/CMakeLists.txt Tue Sep 27 11:37:30 2016<br class="gmail_msg"><br>@@ -53,6 +53,7 @@ set(ADTSources<br class="gmail_msg"><br>   SparseBitVectorTest.cpp<br class="gmail_msg"><br>   SparseMultiSetTest.cpp<br class="gmail_msg"><br>   SparseSetTest.cpp<br class="gmail_msg"><br>+  StringExtrasTest.cpp<br class="gmail_msg"><br>   StringMapTest.cpp<br class="gmail_msg"><br>   StringRefTest.cpp<br class="gmail_msg"><br>   TinyPtrVectorTest.cpp<br class="gmail_msg"><br><br class="gmail_msg"><br>Added: llvm/trunk/unittests/ADT/StringExtrasTest.cpp<br class="gmail_msg"><br>URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/StringExtrasTest.cpp?rev=282502&view=auto" rel="noreferrer" class="gmail_msg" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ADT/StringExtrasTest.cpp?rev=282502&view=auto</a><br class="gmail_msg"><br>==============================================================================<br class="gmail_msg"><br>--- llvm/trunk/unittests/ADT/StringExtrasTest.cpp (added)<br class="gmail_msg"><br>+++ llvm/trunk/unittests/ADT/StringExtrasTest.cpp Tue Sep 27 11:37:30 2016<br class="gmail_msg"><br>@@ -0,0 +1,52 @@<br class="gmail_msg"><br>+//===- StringExtrasTest.cpp - Unit tests for String extras ----------------===//<br class="gmail_msg"><br>+//<br class="gmail_msg"><br>+//                     The LLVM Compiler Infrastructure<br class="gmail_msg"><br>+//<br class="gmail_msg"><br>+// This file is distributed under the University of Illinois Open Source<br class="gmail_msg"><br>+// License. See LICENSE.TXT for details.<br class="gmail_msg"><br>+//<br class="gmail_msg"><br>+//===----------------------------------------------------------------------===//<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+#include "llvm/ADT/StringExtras.h"<br class="gmail_msg"><br>+#include "gtest/gtest.h"<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+using namespace llvm;<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+TEST(StringExtrasTest, Join) {<br class="gmail_msg"><br>+  std::vector<std::string> Items;<br class="gmail_msg"><br>+  EXPECT_EQ("", join(Items.begin(), Items.end(), " <sep> "));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  Items = {"foo"};<br class="gmail_msg"><br>+  EXPECT_EQ("foo", join(Items.begin(), Items.end(), " <sep> "));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  Items = {"foo", "bar"};<br class="gmail_msg"><br>+  EXPECT_EQ("foo <sep> bar", join(Items.begin(), Items.end(), " <sep> "));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  Items = {"foo", "bar", "baz"};<br class="gmail_msg"><br>+  EXPECT_EQ("foo <sep> bar <sep> baz",<br class="gmail_msg"><br>+            join(Items.begin(), Items.end(), " <sep> "));<br class="gmail_msg"><br>+}<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+TEST(StringExtrasTest, JoinItems) {<br class="gmail_msg"><br>+  const char *Foo = "foo";<br class="gmail_msg"><br>+  std::string Bar = "bar";<br class="gmail_msg"><br>+  llvm::StringRef Baz = "baz";<br class="gmail_msg"><br>+  char X = 'x';<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("", join_items(" <sep> "));<br class="gmail_msg"><br>+  EXPECT_EQ("", join_items('/'));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("foo", join_items(" <sep> ", Foo));<br class="gmail_msg"><br>+  EXPECT_EQ("foo", join_items('/', Foo));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("foo <sep> bar", join_items(" <sep> ", Foo, Bar));<br class="gmail_msg"><br>+  EXPECT_EQ("foo/bar", join_items('/', Foo, Bar));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("foo <sep> bar <sep> baz", join_items(" <sep> ", Foo, Bar, Baz));<br class="gmail_msg"><br>+  EXPECT_EQ("foo/bar/baz", join_items('/', Foo, Bar, Baz));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("foo <sep> bar <sep> baz <sep> x",<br class="gmail_msg"><br>+            join_items(" <sep> ", Foo, Bar, Baz, X));<br class="gmail_msg"><br>+<br class="gmail_msg"><br>+  EXPECT_EQ("foo/bar/baz/x", join_items('/', Foo, Bar, Baz, X));<br class="gmail_msg"></blockquote><div><br>FWIW I wouldn't feel the need to test each of these 8 cases.<br><br>For example, it looks like the ability for join_items to accept different kinds of separators is orthogonal to how it accepts the things to join. So I'd probably only test one instance of each of the separators (" <sep> " and '/') in a case where they appear at all, then just test the other cases with a single separator chosen semi-randomly (probably a short string, like '/' or ',', though, to make the tests simpler)<br><br>Ever the 4 cases of {{Foo}, {Foo, Bar}, {Foo, Bar, Baz}, {Foo, Bar, Baz, X} seem a bit verbose. What does each cover that the others don't? Perhaps just testing the empty case, the single item case, and {Foo, Bar, Baz, X} ? (skip the {Foo, Bar}, and {Foo, Bar, Baz} cases?) You could maybe even test {foo} as the single case and {Bar, Baz, X} in the other.<br><br>Could even decide that testing all string-able things isn't necessary (it's not a closed set anyway) & skip some of these. Or write a custom streamable type that has no other features and assume that if that works, everything else does (because we already know they're streamable - their docs say they are, etc, we don't need to test that they are)<br><br>Just some ideas, in case any sound good to you,<br><br>- Dave<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>+}<br class="gmail_msg"><br><br class="gmail_msg"><br><br class="gmail_msg"><br>_______________________________________________<br class="gmail_msg"><br>llvm-commits mailing list<br class="gmail_msg"><br><a href="mailto:llvm-commits@lists.llvm.org" class="gmail_msg" target="_blank">llvm-commits@lists.llvm.org</a><br class="gmail_msg"><br><a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" class="gmail_msg" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br class="gmail_msg"><br></blockquote></div></div>