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
diff --git a/include/base/strings.h b/include/base/strings.h
index 5dbc5fb..638f845 100644
--- a/include/base/strings.h
+++ b/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 @@
 // 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);