Index: include/llvm/ADT/StringExtras.h =================================================================== --- include/llvm/ADT/StringExtras.h +++ include/llvm/ADT/StringExtras.h @@ -155,6 +155,8 @@ /// it if it is not printable or if it is an escape char. void PrintEscapedString(StringRef Name, raw_ostream &Out); +namespace detail { + template inline std::string join_impl(IteratorT Begin, IteratorT End, StringRef Separator, std::input_iterator_tag) { @@ -189,12 +191,61 @@ return S; } +template +inline void join_items_impl(std::string &Result, Sep Separator) {} + +template +inline void join_items_impl(std::string &Result, Sep Separator, + const Arg &Item) { + Result += Item; +} + +template +inline void join_items_impl(std::string &Result, Sep Separator, const Arg1 &A1, + const Arg2 &A2, Args &&... Items) { + Result += A1; + Result += Separator; + join_items_impl(Result, Separator, A2, std::forward(Items)...); +} + +inline size_t join_one_item_size(char C) { return 1; } +inline size_t join_one_item_size(const char *S) { return S ? ::strlen(S) : 0; } + +template inline size_t join_one_item_size(const T &Str) { + return Str.size(); +} + +inline size_t join_items_size() { return 0; } + +template inline size_t join_items_size(const A1 &A) { + return join_one_item_size(A); +} +template +inline size_t join_items_size(const A1 &A, const A2 &B, Args &&... Items) { + return join_one_item_size(A) + + join_items_size(B, std::forward(Items)...); +} +} + /// Joins the strings in the range [Begin, End), adding Separator between /// the elements. template inline std::string join(IteratorT Begin, IteratorT End, StringRef Separator) { typedef typename std::iterator_traits::iterator_category tag; - return join_impl(Begin, End, Separator, tag()); + return detail::join_impl(Begin, End, Separator, tag()); +} + +template +inline std::string join_items(Sep Separator, Args &&... Items) { + std::string Result; + if (sizeof...(Items) == 0) + return Result; + + size_t NS = detail::join_one_item_size(Separator); + size_t NI = detail::join_items_size(std::forward(Items)...); + Result.reserve(NI + (sizeof...(Items) - 1) * NS); + detail::join_items_impl(Result, Separator, std::forward(Items)...); + return Result; } } // End llvm namespace Index: unittests/ADT/CMakeLists.txt =================================================================== --- unittests/ADT/CMakeLists.txt +++ unittests/ADT/CMakeLists.txt @@ -53,6 +53,7 @@ SparseBitVectorTest.cpp SparseMultiSetTest.cpp SparseSetTest.cpp + StringExtrasTest.cpp StringMapTest.cpp StringRefTest.cpp TinyPtrVectorTest.cpp Index: unittests/ADT/StringExtrasTest.cpp =================================================================== --- /dev/null +++ unittests/ADT/StringExtrasTest.cpp @@ -0,0 +1,52 @@ +//===- StringExtrasTest.cpp - Unit tests for String extras ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringExtras.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(StringExtrasTest, Join) { + std::vector Items; + EXPECT_EQ("", join(Items.begin(), Items.end(), " ")); + + Items = {"foo"}; + EXPECT_EQ("foo", join(Items.begin(), Items.end(), " ")); + + Items = {"foo", "bar"}; + EXPECT_EQ("foo bar", join(Items.begin(), Items.end(), " ")); + + Items = {"foo", "bar", "baz"}; + EXPECT_EQ("foo bar baz", + join(Items.begin(), Items.end(), " ")); +} + +TEST(StringExtrasTest, JoinItems) { + const char *Foo = "foo"; + std::string Bar = "bar"; + llvm::StringRef Baz = "baz"; + char X = 'x'; + + EXPECT_EQ("", join_items(" ")); + EXPECT_EQ("", join_items('/')); + + EXPECT_EQ("foo", join_items(" ", Foo)); + EXPECT_EQ("foo", join_items('/', Foo)); + + EXPECT_EQ("foo bar", join_items(" ", Foo, Bar)); + EXPECT_EQ("foo/bar", join_items('/', Foo, Bar)); + + EXPECT_EQ("foo bar baz", join_items(" ", Foo, Bar, Baz)); + EXPECT_EQ("foo/bar/baz", join_items('/', Foo, Bar, Baz)); + + EXPECT_EQ("foo bar baz x", + join_items(" ", Foo, Bar, Baz, X)); + + EXPECT_EQ("foo/bar/baz/x", join_items('/', Foo, Bar, Baz, X)); +}