diff --git a/src/clapp/option_container.cpp b/src/clapp/option_container.cpp
index 06f2ec45227b846119b40bcc4b597ecffb748841..dda9d37557a6d793e0cd225413c41c9db4356f91 100644
--- a/src/clapp/option_container.cpp
+++ b/src/clapp/option_container.cpp
@@ -145,18 +145,6 @@ void clapp::parser::basic_option_container_t::validate_options() const {
     }
 }
 
-namespace clapp {
-static std::string stringify(
-    std::optional<std::vector<std::string>>& opt_str_vec) {
-    return std::accumulate(opt_str_vec.value().begin(),
-                           opt_str_vec.value().end(), std::string{},
-                           [](std::string& lhs, const std::string& rhs) {
-                               return lhs.empty() ? (rhs) : (lhs + " " + rhs);
-                           });
-}
-
-}  // namespace clapp
-
 void clapp::parser::basic_option_container_t::validate_options_xor(
     const types::variant_opt_conf_container_t* options,
     const std::string& options_str,
diff --git a/src/include/clapp/value.h b/src/include/clapp/value.h
index 70816a8100a7a193ceaa1536045daee7861c9693..7c8fd52c7c90eb3e6e416c91197aa4dd5cc85e95 100644
--- a/src/include/clapp/value.h
+++ b/src/include/clapp/value.h
@@ -19,6 +19,7 @@
 #include <clapp/filesystem.h>
 
 #include <functional>
+#include <numeric>
 #include <optional>
 #include <string>
 #include <string_view>
@@ -106,6 +107,11 @@ class path_exists_t {
 };
 #endif
 
+inline std::string concat_str(const std::string &lhs, const std::string &rhs);
+
+inline std::string stringify(
+    const std::optional<std::vector<std::string>> &opt_str_vec);
+
 }  // namespace value
 
 }  // namespace clapp
diff --git a/src/include/clapp/value.hpp b/src/include/clapp/value.hpp
index c455f608267b40651298c6498452ccf0b6ec2bb0..c86ec8734a278024ab79b4871756ccf2e7ebcf94 100644
--- a/src/include/clapp/value.hpp
+++ b/src/include/clapp/value.hpp
@@ -204,4 +204,16 @@ clapp::fs::path clapp::value::convert_value<clapp::fs::path>(
     std::string_view param);
 #endif
 
+std::string clapp::value::concat_str(const std::string& lhs,
+                                     const std::string& rhs) {
+    return lhs.empty() ? (rhs) : (lhs + " " + rhs);
+}
+
+std::string clapp::value::stringify(
+    const std::optional<std::vector<std::string>>& opt_str_vec) {
+    return std::accumulate(opt_str_vec.value().begin(),
+                           opt_str_vec.value().end(), std::string{},
+                           concat_str);
+}
+
 #endif
diff --git a/tests/value.cpp b/tests/value.cpp
index c5e0f4b163f0866ae28ff2f83ee16903a5f43177..47b53cba9045a02dd04dc8cf4927247a1ccff94a 100644
--- a/tests/value.cpp
+++ b/tests/value.cpp
@@ -501,3 +501,34 @@ TEST(value, foundFuncTReturnsExit) {
     ASSERT_THAT(ret.value().get_exit_code(), testing::Eq(exit_code));
     ASSERT_THAT(stringstr.str(), testing::StrEq("called func-name2"));
 }
+
+TEST(value, concatStr) {
+    ASSERT_THAT(clapp::value::concat_str("", "b"), testing::StrEq("b"));
+    ASSERT_THAT(clapp::value::concat_str("a", "b"), testing::StrEq("a b"));
+    ASSERT_THAT(clapp::value::concat_str("a", ""), testing::StrEq("a "));
+}
+
+TEST(value, stringifyNulloptThrows) {
+    const std::optional<std::vector<std::string>> opt_str_vec{std::nullopt};
+    ASSERT_THROW(clapp::value::stringify(opt_str_vec),
+                 std::bad_optional_access);
+}
+
+TEST(value, stringifyEmptyVec) {
+    const std::optional<std::vector<std::string>> opt_str_vec{
+        std::vector<std::string>{}};
+    ASSERT_THAT(clapp::value::stringify(opt_str_vec), testing::StrEq(""));
+}
+
+TEST(value, stringifyVecWithOneElement) {
+    const std::optional<std::vector<std::string>> opt_str_vec{
+        std::vector<std::string>{"one"}};
+    ASSERT_THAT(clapp::value::stringify(opt_str_vec), testing::StrEq("one"));
+}
+
+TEST(value, stringifyVecWithTwoElements) {
+    const std::optional<std::vector<std::string>> opt_str_vec{
+        std::vector<std::string>{"one", "two"}};
+    ASSERT_THAT(clapp::value::stringify(opt_str_vec),
+                testing::StrEq("one two"));
+}