[libcamera-devel,v4,02/11] libcamera: utils: Add string join function

Message ID 20200404004438.17992-3-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • Simple pipeline handler
Related show

Commit Message

Laurent Pinchart April 4, 2020, 12:44 a.m. UTC
Add a utils::join() function to join elements of a container into a
string, with a separator and an optional conversion function if the
elements are not implicitly convertible to std::string.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
---
Changes since v3:

- Add a test for the conversion function
---
 src/libcamera/include/utils.h | 44 +++++++++++++++++++++++++++++++++++
 src/libcamera/utils.cpp       | 16 +++++++++++++
 test/utils.cpp                | 20 +++++++++++++++-
 3 files changed, 79 insertions(+), 1 deletion(-)

Patch

diff --git a/src/libcamera/include/utils.h b/src/libcamera/include/utils.h
index cfa620f24581..242eeded9d50 100644
--- a/src/libcamera/include/utils.h
+++ b/src/libcamera/include/utils.h
@@ -11,6 +11,7 @@ 
 #include <chrono>
 #include <memory>
 #include <ostream>
+#include <sstream>
 #include <string>
 #include <string.h>
 #include <sys/time.h>
@@ -109,6 +110,49 @@  inline _hex hex<uint64_t>(uint64_t value, unsigned int width)
 
 size_t strlcpy(char *dst, const char *src, size_t size);
 
+#ifndef __DOXYGEN__
+template<typename Container, typename UnaryOp>
+std::string join(const Container &items, const std::string &sep, UnaryOp op)
+{
+	std::ostringstream ss;
+	bool first = true;
+
+	for (typename Container::const_iterator it = std::begin(items);
+	     it != std::end(items); ++it) {
+		if (!first)
+			ss << sep;
+		else
+			first = false;
+
+		ss << op(*it);
+	}
+
+	return ss.str();
+}
+
+template<typename Container>
+std::string join(const Container &items, const std::string &sep)
+{
+	std::ostringstream ss;
+	bool first = true;
+
+	for (typename Container::const_iterator it = std::begin(items);
+	     it != std::end(items); ++it) {
+		if (!first)
+			ss << sep;
+		else
+			first = false;
+
+		ss << *it;
+	}
+
+	return ss.str();
+}
+#else
+template<typename Container, typename UnaryOp>
+std::string join(const Container &items, const std::string &sep, UnaryOp op = nullptr);
+#endif
+
 namespace details {
 
 class StringSplitter
diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
index 58ee7cc19c8c..97f9b632e45b 100644
--- a/src/libcamera/utils.cpp
+++ b/src/libcamera/utils.cpp
@@ -297,6 +297,22 @@  details::StringSplitter::iterator details::StringSplitter::end() const
 	return iterator(this, std::string::npos);
 }
 
+/**
+ * \fn template<typename Container, typename UnaryOp> \
+ * std::string utils::join(const Container &items, const std::string &sep, UnaryOp op)
+ * \brief Join elements of a container in a string with a separator
+ * \param[in] items The container
+ * \param[in] sep The separator to add between elements
+ * \param[in] op A function that converts individual elements to strings
+ *
+ * This function joins all elements in the \a items container into a string and
+ * returns it. The \a sep separator is added between elements. If the container
+ * elements are not implicitly convertible to std::string, the \a op function
+ * shall be provided to perform conversion of elements to std::string.
+ *
+ * \return A string that concatenates all elements in the container
+ */
+
 /**
  * \fn split(const std::string &str, const std::string &delim)
  * \brief Split a string based on a delimiter
diff --git a/test/utils.cpp b/test/utils.cpp
index 58816f153066..55ce9365a53e 100644
--- a/test/utils.cpp
+++ b/test/utils.cpp
@@ -10,6 +10,8 @@ 
 #include <string>
 #include <vector>
 
+#include <libcamera/geometry.h>
+
 #include "test.h"
 #include "utils.h"
 
@@ -99,7 +101,7 @@  protected:
 			return TestFail;
 		}
 
-		/* utils::split() test. */
+		/* utils::join() and utils::split() test. */
 		std::vector<std::string> elements = {
 			"/bin",
 			"/usr/bin",
@@ -111,6 +113,11 @@  protected:
 		for (const auto &element : elements)
 			path += (path.empty() ? "" : ":") + element;
 
+		if (path != utils::join(elements, ":")) {
+			cerr << "utils::join() test failed" << endl;
+			return TestFail;
+		}
+
 		std::vector<std::string> dirs;
 
 		for (const auto &dir : utils::split(path, ":"))
@@ -121,6 +128,17 @@  protected:
 			return TestFail;
 		}
 
+		/* utils::join() with conversion function test. */
+		std::vector<Size> sizes = { { 0, 0 }, { 100, 100 } };
+		s = utils::join(sizes, "/", [](const Size &size) {
+					return size.toString();
+				});
+
+		if (s != "0x0/100x100") {
+			cerr << "utils::join() with conversion test failed" << endl;
+			return TestFail;
+		}
+
 		/* utils::dirname() tests. */
 		if (TestPass != testDirname())
 			return TestFail;