From a0987ff8ade47357987ec52fc884881ac97da72e Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 17 Aug 2023 11:06:44 +0100 Subject: [PATCH] [Feature] Add utility to split string like stuff for C++ code No ranges, as they are a bit ugly to use yet --- src/libutil/cxx/util.hxx | 43 ++++++++++++++++++++++++++++++---- src/libutil/cxx/util_tests.cxx | 39 ++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/libutil/cxx/util.hxx b/src/libutil/cxx/util.hxx index 9ef2f6295..4ec1d568c 100644 --- a/src/libutil/cxx/util.hxx +++ b/src/libutil/cxx/util.hxx @@ -1,11 +1,11 @@ -/*- - * Copyright 2021 Vsevolod Stakhov +/* + * Copyright 2023 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -53,8 +53,8 @@ constexpr auto find_map(const C &c, const K &k) -> std::optional -inline constexpr auto make_string_view_from_it(_It begin, _It end) +template +inline constexpr auto make_string_view_from_it(It begin, It end) { using result_type = std::string_view; @@ -91,6 +91,39 @@ inline auto string_foreach_line(const S &input, const F &functor) } } +/** + * Iterate over elements in a string + * @tparam S + * @tparam F + * @param input + * @param delim + * @param functor + * @param ignore_empty + * @return + */ +template && std::is_constructible_v && std::is_constructible_v, bool> = true> +inline auto string_foreach_delim(const S &input, const D &delim, const F &functor, const bool ignore_empty = true) +{ + size_t first = 0; + auto sv_input = std::string_view{input}; + auto sv_delim = std::string_view{delim}; + + while (first < sv_input.size()) { + const auto second = sv_input.find_first_of(sv_delim, first); + + if (first != second || !ignore_empty) { + functor(sv_input.substr(first, second - first)); + } + + if (second == std::string_view::npos) { + break; + } + + first = second + 1; + } +} + template, bool> = true> inline auto string_split_on(const S &input, std::string_view::value_type chr) -> std::pair { diff --git a/src/libutil/cxx/util_tests.cxx b/src/libutil/cxx/util_tests.cxx index 2b3092779..83b293ce5 100644 --- a/src/libutil/cxx/util_tests.cxx +++ b/src/libutil/cxx/util_tests.cxx @@ -1,11 +1,11 @@ -/*- +/* * Copyright 2023 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -43,4 +43,39 @@ TEST_SUITE("cxx utils") CHECK(res.second == expected.second); } } + + TEST_CASE("string_foreach_delim") + { + std::tuple, std::vector>> cases[] = { + {"test"sv, ","sv, {{"test"}, {"test"}}}, + {"test,test"sv, ","sv, {{"test", "test"}, {"test", "test"}}}, + {"test, test"sv, ", "sv, {{"test", "test"}, {"test", "", "test"}}}, + {"test, test,,"sv, ", "sv, {{"test", "test"}, {"test", "", "test", ""}}}, + }; + + for (const auto &c: cases) { + auto res = std::vector(); + string_foreach_delim(std::get<0>(c), std::get<1>(c), [&](const auto &v) { + res.push_back(v); + }); + + auto compare_vec = [](const std::vector &v1, const std::vector &v2) { + CHECK(v1.size() == v2.size()); + for (size_t i = 0; i < v1.size(); ++i) { + CHECK(v1[i] == v2[i]); + } + }; + + compare_vec(res, std::get<2>(c).first); + + res.clear(); + // Perform the same test but with no skip empty + string_foreach_delim( + std::get<0>(c), std::get<1>(c), [&](const auto &v) { + res.push_back(v); + }, + false); + compare_vec(res, std::get<2>(c).second); + } + } } \ No newline at end of file -- 2.39.5