diff --git a/src/util/string.cpp b/src/util/string.cpp index 6045a1a..6c576f7 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -1,6 +1,7 @@ #include "string.hpp" #include +#include namespace gz::util { @@ -25,7 +26,34 @@ std::vector splitStringInVector(std::string& s, char separator) { } } return v; - } + +template +std::vector splitStringInVector(const std::string_view& s, const std::string& separator, bool skipEmptyStrings) { + std::vector v; + + std::string::size_type posStart = 0; + std::string::size_type posEnd = s.find(separator, posStart); + while (posEnd != std::string::npos) { + if (!(skipEmptyStrings and posStart == posEnd)) { + v.emplace_back(T(s.begin() + posStart, s.begin() + posEnd)); + } + posStart = posEnd + 1; + posEnd = s.find(separator, posStart); + } + // last element + if (posStart < s.size()) { + v.emplace_back(T(s.begin() + posStart, s.end())); + } + // if last char is separator, append empty string + else if (!skipEmptyStrings and posStart == s.size()) { + v.emplace_back(T(s.begin(), s.begin())); + } + return v; +} + +template std::vector splitStringInVector(const std::string_view&, const std::string&, bool); +template std::vector splitStringInVector(const std::string_view&, const std::string&, bool); + } // namespace gz::util diff --git a/src/util/string.hpp b/src/util/string.hpp index 18808d2..296c5d9 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -3,13 +3,33 @@ #include #include #include +#include namespace gz::util { + template + concept SplitStringInVectorImplemented = std::same_as || std::same_as; /** - * @todo document + * @brief Split a string at a separator into a vector + * @details + * Splits a string at a separator.\n + * Two templates exist, one where the vector will hold std::string_views into s + * and one where it will hold std::strings. + * Behavior: + * - the elements of the returned vector will not contain the separator. + * - if the separator is not in s, the vector will contain s as only element. + * - empty elements will occur if: + * - separator is the first char + * - separator is the last char + * - two or more separator appear after each other\n + * You can turn this behaviour off by setting skipEmptyStrings to true + * @note + * The string_view variant has the advantage of being faster, however:\n + * The string_views will reference the original string, so it must not be changed or destroyed + * as long as the string_views are used! */ - std::vector splitStringInVector(std::string& s, char separator = ','); + template + std::vector splitStringInVector(const std::string_view& s, const std::string& separator, bool skipEmptyStrings=false); /** * @name Map with string type as key, works with strings, string_view and char* @@ -24,8 +44,27 @@ namespace gz::util { size_t operator()(std::string_view str) const { return hash_type{}(str); } size_t operator()(std::string const& str) const { return hash_type{}(str); } }; + /** + * @brief A unordered_map where you can use string_views to access elements + * @details + * To retrieve an element using a string view, you need to do this: + * @code + * std::string_view sv = ...; + * gz::util::unordered_string_map smap = { ... }; + * ... + * if (smap.contains(sv)) { + * return smap.find(sv)->second; + * } + * @endcode + * The at() member and [] operator do not work with the string_view. + */ template using unordered_string_map = std::unordered_map>; + /** + * @brief same as unordered_string_map, but using std::map instead of std::unordered_map + */ + template + using string_map = std::map>; /** * @} */