| //===----------------------------------------------------------------------===// |
| // |
| // 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 <algorithm> |
| #include <array> |
| #include <cassert> |
| #include <concepts> |
| #include <map> |
| #include <ranges> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "test_macros.h" |
| |
| template <std::ranges::view View, std::ranges::range Expected> |
| constexpr bool is_equal(View& view, const Expected& expected) { |
| return std::ranges::equal(view, expected, std::ranges::equal); |
| } |
| |
| template <class T, class Separator, class U, std::size_t M> |
| constexpr bool test_function_call(T&& input, Separator&& separator, std::array<U, M> expected) { |
| std::ranges::split_view v(input, separator); |
| return is_equal(v, expected); |
| } |
| |
| template <class T, class Separator, class U, std::size_t M> |
| constexpr bool test_with_piping(T&& input, Separator&& separator, std::array<U, M> 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 <class T> |
| constexpr std::string_view sv(T&& str) { |
| return std::string_view(str); |
| }; |
| |
| template <class T, class Separator, class U, std::size_t M> |
| constexpr void test_one(T&& input, Separator&& separator, std::array<U, M> 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<std::string, int>; |
| using Vec = std::vector<Map>; |
| |
| 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<std::string_view, 0> 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<std::string_view> 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; |
| } |