summaryrefslogtreecommitdiff
path: root/base
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2015-05-21 18:37:36 -0700
committerDan Albert <danalbert@google.com>2015-05-22 10:07:06 -0700
commite0da8a1d377c22d1956376302294ee988b44fd47 (patch)
tree0813d979ad3b68c1e0269f7caca54651b8933471 /base
parent3ff23e2461de5a5aae4343147486df84e8a77d90 (diff)
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
Diffstat (limited to 'base')
-rw-r--r--base/include/base/strings.h22
-rw-r--r--base/strings.cpp23
-rw-r--r--base/strings_test.cpp13
3 files changed, 36 insertions, 22 deletions
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 <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);
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 <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;
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 <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("", ""));