2828
2929#include " harness_export.h"
3030
31- #include < numeric> // accumulate
3231#include < string>
3332#include < vector>
3433
@@ -40,118 +39,56 @@ std::vector<std::string> HARNESS_EXPORT wrap_string(const std::string &to_wrap,
4039 std::size_t width,
4140 std::size_t indent_size);
4241
43- /* * @brief Checks whether string ends with the specified suffix
44- *
45- * Returns true if the string ends with the given suffix.
46- *
47- * @return bool
48- */
49- bool HARNESS_EXPORT ends_with (const std::string &str,
50- const std::string &suffix);
51-
52- /* * @brief Checks whether string starts with the specified prefix
53- *
54- * Returns true if the string begins with the given prefix.
55- *
56- * @return bool
57- */
58- bool HARNESS_EXPORT starts_with (const std::string &str,
59- const std::string &prefix);
60-
6142HARNESS_EXPORT
6243MY_ATTRIBUTE ((format(printf, 1 , 2 )))
6344std::string string_format (const char *format, ...);
6445
6546} // namespace utility
6647
6748namespace detail {
68- template <class Container , class T >
69- struct Join {
70- static std::string impl (Container, const std::string &);
71- };
72-
73- template <class Container >
74- struct Join <Container, std::string> {
75- static std::string impl (Container cont, const std::string &delim) {
76- if (cont.begin () == cont.end ()) return {};
77-
78- std::string o (*(cont.begin ()));
79-
80- // if T::value_type has a .size() method reallocs can be avoided
81- // when joining the strings by calculating the size upfront
82- {
83- const size_t delim_size = delim.size ();
84- size_t space =
85- std::accumulate (std::next (cont.begin ()), cont.end (), o.size (),
86- [delim_size](size_t sum, const std::string &b) {
87- return sum + delim_size + b.size ();
88- });
89- o.reserve (space);
90- }
91-
92- #if 0
93- // once benchmarked that this is equivalent of the hand-rolled version
94- // (number of allocs, ...) this implementation could be used.
95- return std::accumulate(std::next(cont.begin()), cont.end(), o,
96- [&delim](std::string a, const std::string &b) {
97- return a.append(delim).append(b);
98- });
99- #else
100- // add the first element directly
101- auto it = std::next (cont.begin ());
102- const auto last = cont.end ();
103-
104- // all other elements with delim
105- for (; it != last; ++it) {
106- o += delim;
107-
108- o += *it;
109- }
110-
111- return o;
112- #endif
113- }
114- };
115-
116- template <class Container >
117- struct Join <Container, const char *> {
118- static std::string impl (Container cont, const std::string &delim) {
119- if (cont.begin () == cont.end ()) return {};
120-
121- return std::accumulate (std::next (cont.begin ()), cont.end (),
122- std::string (*(cont.begin ())),
123- [&delim](const std::string &a, const char *b) {
124- return a + delim + b;
125- });
126- }
127- };
49+
50+ // a simplified variant of the std::ranges::range concept
51+ //
52+ // C++20 ranges lib isn't fully available yet.
53+
54+ template <class R >
55+ concept range = requires (const R &rng) {
56+ std::begin (rng);
57+ std::end (rng);
58+ };
12859
12960} // namespace detail
13061
13162/* *
132- * join elements of an container into a string separated by a delimiter.
133- *
134- * Container MUST:
63+ * join elements of a range into a string separated by a delimiter.
13564 *
136- * - have .begin() and end()
137- * - ::iterator must be ForwardIterator + InputIterator
138- * - ::value_type must be appendable to std::string
65+ * works with:
13966 *
140- * should work with:
67+ * - std::vector, std::array, c-array, list, forward_list, deque
68+ * - and std::string, c-string, std::string_view
14169 *
142- * - std::vector<const char *|std::string>
143- * - std::array<const char *|std::string, N>
144- * - std::list<const char *|std::string>
145- *
146- * @param cont a container
70+ * @param rng a range of strings
14771 * @param delim delimiter
148- * @returns string of elements of container separated by delim
72+ * @returns string of elements of the range separated by delim
14973 */
150- template <class Container >
151- std::string join (Container cont, const std::string &delim) {
152- return detail::Join<Container, typename Container::value_type>::impl (cont,
153- delim);
74+ std::string join (const detail::range auto &rng, std::string_view delim)
75+ requires(std::constructible_from<std::string, decltype (*std::begin (rng))>)
76+ {
77+ auto cur = std::begin (rng);
78+ const auto end = std::end (rng);
79+
80+ if (cur == end) return {}; // empty
81+
82+ std::string joined (*cur); // first element.
83+
84+ // append delim + element
85+ for (cur = std::next (cur); cur != end; cur = std::next (cur)) {
86+ joined.append (delim).append (*cur);
87+ }
88+
89+ return joined;
15490}
91+
15592/* Checks that given string belongs to the collection of strings */
15693template <class T >
15794constexpr bool str_in_collection (const T &t, const std::string_view &k) {
0 commit comments