Generalize Join to work for any container/element.

This is more scalable than explicitly instantiating templates for the
cross product of containers and element types.

Specifically I'm adding this so I can join an unordered_set in adb.

Change-Id: I0055f3390a0ff26a886a0d41bbf0d4fe3d210f9c
This commit is contained in:
Dan Albert 2015-05-21 18:37:36 -07:00
parent 3ff23e2461
commit e0da8a1d37
3 changed files with 36 additions and 22 deletions

View File

@ -17,6 +17,7 @@
#ifndef BASE_STRINGS_H
#define BASE_STRINGS_H
#include <sstream>
#include <string>
#include <vector>
@ -34,9 +35,24 @@ std::vector<std::string> 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 <typename StringT>
std::string Join(const std::vector<StringT>& strings, char separator);
// Joins a container of things into a single string, using the given separator.
template <typename ContainerT>
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<std::string>&, char);
extern template std::string Join(const std::vector<const char*>&, char);
// Tests whether 's' starts with 'prefix'.
bool StartsWith(const std::string& s, const char* prefix);

View File

@ -79,25 +79,10 @@ std::string Trim(const std::string& s) {
return s.substr(start_index, end_index - start_index + 1);
}
template <typename StringT>
std::string Join(const std::vector<StringT>& 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<std::string>(const std::vector<std::string>& strings,
char separator);
template std::string Join<const char*>(const std::vector<const char*>& 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<std::string>&, char);
template std::string Join(const std::vector<const char*>&, char);
bool StartsWith(const std::string& s, const char* prefix) {
return s.compare(0, strlen(prefix), prefix) == 0;

View File

@ -20,6 +20,8 @@
#include <string>
#include <vector>
#include <set>
#include <unordered_set>
TEST(strings, split_empty) {
std::vector<std::string> 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<int> list = {1, 2, 3};
ASSERT_EQ("1,2,3", android::base::Join(list, ','));
}
TEST(strings, join_unordered_set) {
std::unordered_set<int> 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("", ""));