diff --git a/base/include/base/strings.h b/base/include/base/strings.h index 5dbc5fbfb..638f845db 100644 --- a/base/include/base/strings.h +++ b/base/include/base/strings.h @@ -17,6 +17,7 @@ #ifndef BASE_STRINGS_H #define BASE_STRINGS_H +#include #include #include @@ -34,9 +35,24 @@ std::vector Split(const std::string& s, // Trims whitespace off both ends of the given string. std::string Trim(const std::string& s); -// Joins a vector of strings into a single string, using the given separator. -template -std::string Join(const std::vector& strings, char separator); +// Joins a container of things into a single string, using the given separator. +template +std::string Join(const ContainerT& things, char separator) { + if (things.empty()) { + return ""; + } + + std::ostringstream result; + result << *things.begin(); + for (auto it = std::next(things.begin()); it != things.end(); ++it) { + result << separator << *it; + } + return result.str(); +} + +// We instantiate the common cases in strings.cpp. +extern template std::string Join(const std::vector&, char); +extern template std::string Join(const std::vector&, char); // Tests whether 's' starts with 'prefix'. bool StartsWith(const std::string& s, const char* prefix); diff --git a/base/strings.cpp b/base/strings.cpp index d3375d9fa..bac983b01 100644 --- a/base/strings.cpp +++ b/base/strings.cpp @@ -79,25 +79,10 @@ std::string Trim(const std::string& s) { return s.substr(start_index, end_index - start_index + 1); } -template -std::string Join(const std::vector& strings, char separator) { - if (strings.empty()) { - return ""; - } - - std::string result(strings[0]); - for (size_t i = 1; i < strings.size(); ++i) { - result += separator; - result += strings[i]; - } - return result; -} - -// Explicit instantiations. -template std::string Join(const std::vector& strings, - char separator); -template std::string Join(const std::vector& strings, - char separator); +// These cases are probably the norm, so we mark them extern in the header to +// aid compile time and binary size. +template std::string Join(const std::vector&, char); +template std::string Join(const std::vector&, char); bool StartsWith(const std::string& s, const char* prefix) { return s.compare(0, strlen(prefix), prefix) == 0; diff --git a/base/strings_test.cpp b/base/strings_test.cpp index 46a1ab543..5f675750c 100644 --- a/base/strings_test.cpp +++ b/base/strings_test.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include TEST(strings, split_empty) { std::vector parts = android::base::Split("", ","); @@ -121,6 +123,17 @@ TEST(strings, join_separator_in_vector) { ASSERT_EQ(",,,", android::base::Join(list, ',')); } +TEST(strings, join_simple_ints) { + std::set list = {1, 2, 3}; + ASSERT_EQ("1,2,3", android::base::Join(list, ',')); +} + +TEST(strings, join_unordered_set) { + std::unordered_set list = {1, 2}; + ASSERT_TRUE("1,2" == android::base::Join(list, ',') || + "2,1" == android::base::Join(list, ',')); +} + TEST(strings, startswith_empty) { ASSERT_FALSE(android::base::StartsWith("", "foo")); ASSERT_TRUE(android::base::StartsWith("", ""));