//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 // Some basic examples of how split_view might be used in the wild. This is a general // collection of sample algorithms and functions that try to mock general usage of // this view. // These test check the output `split_view` produces for a variety of inputs, including many corner cases, with no // restrictions on which member functions can be called. #include #include #include #include #include #include #include #include #include #include #include "test_macros.h" template constexpr bool is_equal(View& view, const Expected& expected) { return std::ranges::equal(view, expected, std::ranges::equal); } template constexpr bool test_function_call(T&& input, Separator&& separator, std::array expected) { std::ranges::split_view v(input, separator); return is_equal(v, expected); } template constexpr bool test_with_piping(T&& input, Separator&& separator, std::array expected) { auto expected_it = expected.begin(); for (auto e : input | std::ranges::views::split(separator)) { if (expected_it == expected.end()) return false; if (!std::ranges::equal(e, *expected_it)) return false; ++expected_it; } return expected_it == expected.end(); } constexpr bool test_l_r_values() { using namespace std::string_view_literals; // Both lvalues and rvalues can be used as input. { // Lvalues. { auto input = "abc"sv; auto sep = " "sv; [[maybe_unused]] std::ranges::split_view v(input, sep); } // Const lvalues. { const auto input = "abc"sv; const auto sep = " "sv; [[maybe_unused]] std::ranges::split_view v(input, sep); } // Rvalues. { auto input = "abc"sv; auto sep = " "sv; [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep)); } // Const rvalues. { const auto input = "abc"sv; const auto sep = " "sv; [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep)); } } return true; } constexpr bool test_string_literal_separator() { using namespace std::string_view_literals; // Splitting works as expected when the separator is a single character literal. { std::ranges::split_view v("abc def"sv, ' '); assert(is_equal(v, std::array{"abc"sv, "def"sv})); } // Counterintuitively, a seemingly equivalent separator expressed as a string literal doesn't match anything. This is // because of the implicit terminating null in the literal. { std::ranges::split_view v("abc def"sv, " "); assert(is_equal(v, std::array{"abc def"sv})); } // To illustrate the previous point further, the separator is actually a two-character string literal: `{' ', '\0'}`. // Should the input string contain that two-character sequence, the separator would match. { std::ranges::split_view v("abc \0def"sv, " "); assert(is_equal(v, std::array{"abc"sv, "def"sv})); } return true; } // Make sure that a string literal and a `string_view` produce the same results (which isn't always the case, see // below). template constexpr std::string_view sv(T&& str) { return std::string_view(str); }; template constexpr void test_one(T&& input, Separator&& separator, std::array expected) { assert(test_function_call(input, separator, expected)); assert(test_with_piping(input, separator, expected)); } constexpr bool test_string_literals() { // These tests show characteristic examples of how using string literals with `split_view` produces unexpected // results due to the implicit terminating null that is treated as part of the range. using namespace std::string_view_literals; char short_sep = ' '; auto long_sep = "12"sv; // When splitting a string literal, only the last segment will be null-terminated (getting the terminating null from // the original range). { std::array expected = {"abc"sv, std::string_view("def", sizeof("def"))}; assert(test_function_call("abc def", short_sep, expected)); assert(test_with_piping("abc def", short_sep, expected)); assert(test_function_call("abc12def", long_sep, expected)); assert(test_with_piping("abc12def", long_sep, expected)); } // Empty string. { // Because an empty string literal contains an implicit terminating null, the output will contain one segment. std::array expected = {std::string_view("", 1)}; assert(test_function_call("", short_sep, expected)); assert(test_with_piping("", short_sep, expected)); assert(test_function_call("", long_sep, expected)); assert(test_with_piping("", long_sep, expected)); } // Terminating null in the separator -- the character literal `' '` and the seemingly equivalent string literal `" "` // are treated differently due to the presence of an implicit `\0` in the latter. { const char input[] = "abc def"; std::array expected_unsplit = {std::string_view(input, sizeof(input))}; std::array expected_split = {"abc"sv, std::string_view("def", sizeof("def"))}; assert(test_function_call(input, " ", expected_unsplit)); assert(test_function_call("abc \0def", " ", expected_split)); // Note: string literals don't work with piping because arrays decay to pointers, and pointers don't model `range`. } // Empty separator. { auto empty_sep = ""sv; std::array expected = {"a"sv, "b"sv, "c"sv, "\0"sv}; assert(test_function_call("abc", empty_sep, expected)); assert(test_with_piping("abc", empty_sep, expected)); } return true; } bool test_nontrivial_characters() { // Try a deliberately heavyweight "character" type to see if it triggers any corner cases. using Map = std::map; using Vec = std::vector; Map sep = {{"yyy", 999}}; Map m1 = { {"a", 1}, {"bc", 2}, }; Map m2 = { {"def", 3}, }; Map m3 = { {"g", 4}, {"hijk", 5}, }; Vec expected1 = {m1, m2}; Vec expected2 = {m3}; std::ranges::split_view v(Vec{m1, m2, sep, m3}, sep); // Segment 1: {m1, m2} auto outer = v.begin(); assert(outer != v.end()); auto inner = (*outer).begin(); assert(*inner++ == m1); assert(*inner++ == m2); assert(inner == (*outer).end()); // Segment 2: {m3} ++outer; assert(outer != v.end()); inner = (*outer).begin(); assert(*inner++ == m3); assert(inner == (*outer).end()); ++outer; assert(outer == v.end()); return true; } constexpr bool main_test() { using namespace std::string_view_literals; char short_sep = ' '; auto long_sep = "12"sv; // One separator. { std::array expected = {"abc"sv, "def"sv}; test_one("abc def"sv, short_sep, expected); test_one("abc12def"sv, long_sep, expected); } // Several separators in a row. { std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; test_one("abc def"sv, short_sep, expected); test_one("abc12121212def"sv, long_sep, expected); } // Trailing separator. { std::array expected = {"abc"sv, "def"sv, ""sv}; test_one("abc def "sv, short_sep, expected); test_one("abc12def12"sv, long_sep, expected); } // Leading separator. { std::array expected = {""sv, "abc"sv, "def"sv}; test_one(" abc def"sv, short_sep, expected); test_one("12abc12def"sv, long_sep, expected); } // No separator. { std::array expected = {"abc"sv}; test_one("abc"sv, short_sep, expected); test_one("abc"sv, long_sep, expected); } // Input consisting of a single separator. { std::array expected = {""sv, ""sv}; test_one(" "sv, short_sep, expected); test_one("12"sv, long_sep, expected); } // Input consisting of only separators. { std::array expected = {""sv, ""sv, ""sv, ""sv}; test_one(" "sv, short_sep, expected); test_one("121212"sv, long_sep, expected); } // The separator and the string use the same character only. { auto overlapping_sep = "aaa"sv; std::array expected = {""sv, "aa"sv}; test_one("aaaaa"sv, overlapping_sep, expected); } // Many redundant separators. { std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; test_one(" abc def "sv, short_sep, expected); test_one("1212abc121212def1212"sv, long_sep, expected); } // Separators after every character. { std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; test_one(" a b c "sv, short_sep, expected); test_one("12a12b12c12"sv, long_sep, expected); } // Overlap between the separator and the string (see https://wg21.link/lwg3505). { auto overlapping_sep = "ab"sv; std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; test_one("aabaaababb"sv, overlapping_sep, expected); } // Empty input. { std::array expected = {}; test_one(""sv, short_sep, expected); test_one(""sv, long_sep, expected); } // Empty separator. { auto empty_sep = ""sv; std::array expected = {"a"sv, "b"sv, "c"sv}; test_one("abc"sv, empty_sep, expected); test_one("abc"sv, empty_sep, expected); } // Terminating null as a separator. { std::array expected = {"abc"sv, "def"sv}; test_one("abc\0def"sv, '\0', expected); test_one("abc\0\0def"sv, "\0\0"sv, expected); } // Different character types. { // `char`. test_function_call("abc def", ' ', std::array{"abc"sv, "def"sv}); #ifndef TEST_HAS_NO_WIDE_CHARACTERS // `wchar_t`. test_function_call(L"abc def", L' ', std::array{L"abc"sv, L"def"sv}); #endif // `char8_t`. test_function_call(u8"abc def", u8' ', std::array{u8"abc"sv, u8"def"sv}); // `char16_t`. test_function_call(u"abc def", u' ', std::array{u"abc"sv, u"def"sv}); // `char32_t`. test_function_call(U"abc def", U' ', std::array{U"abc"sv, U"def"sv}); } // Non-character input. { std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; test_one(std::array{1, 2, 3, 0, 4, 5, 6}, 0, expected); test_one(std::array{1, 2, 3, 0, 0, 0, 4, 5, 6}, std::array{0, 0, 0}, expected); } return true; } constexpr bool example_test() { // example code in the spec std::string str{"the quick brown fox"}; std::vector result; for (auto r : std::views::split(str, ' ')) { result.emplace_back(r.begin(), r.end()); } using namespace std::string_view_literals; auto expected = {"the"sv, "quick"sv, "brown"sv, "fox"sv}; assert(std::ranges::equal(result, expected)); return true; } int main(int, char**) { example_test(); static_assert(example_test()); test_string_literals(); static_assert(test_string_literals()); test_l_r_values(); static_assert(test_l_r_values()); test_string_literal_separator(); static_assert(test_string_literal_separator()); // Note: map is not `constexpr`, so this test is runtime-only. test_nontrivial_characters(); return 0; }