brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.3 KiB · 5389d93 Raw
396 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// Some basic examples of how split_view might be used in the wild. This is a general12// collection of sample algorithms and functions that try to mock general usage of13// this view.14 15// These test check the output `split_view` produces for a variety of inputs, including many corner cases, with no16// restrictions on which member functions can be called.17 18#include <algorithm>19#include <array>20#include <cassert>21#include <concepts>22#include <map>23#include <ranges>24#include <string>25#include <string_view>26#include <utility>27#include <vector>28 29#include "test_macros.h"30 31template <std::ranges::view View, std::ranges::range Expected>32constexpr bool is_equal(View& view, const Expected& expected) {33  return std::ranges::equal(view, expected, std::ranges::equal);34}35 36template <class T, class Separator, class U, std::size_t M>37constexpr bool test_function_call(T&& input, Separator&& separator, std::array<U, M> expected) {38  std::ranges::split_view v(input, separator);39  return is_equal(v, expected);40}41 42template <class T, class Separator, class U, std::size_t M>43constexpr bool test_with_piping(T&& input, Separator&& separator, std::array<U, M> expected) {44  auto expected_it = expected.begin();45  for (auto e : input | std::ranges::views::split(separator)) {46    if (expected_it == expected.end())47      return false;48    if (!std::ranges::equal(e, *expected_it))49      return false;50 51    ++expected_it;52  }53 54  return expected_it == expected.end();55}56 57constexpr bool test_l_r_values() {58  using namespace std::string_view_literals;59 60  // Both lvalues and rvalues can be used as input.61  {62    // Lvalues.63    {64      auto input = "abc"sv;65      auto sep   = " "sv;66      [[maybe_unused]] std::ranges::split_view v(input, sep);67    }68 69    // Const lvalues.70    {71      const auto input = "abc"sv;72      const auto sep   = " "sv;73      [[maybe_unused]] std::ranges::split_view v(input, sep);74    }75 76    // Rvalues.77    {78      auto input = "abc"sv;79      auto sep   = " "sv;80      [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep));81    }82 83    // Const rvalues.84    {85      const auto input = "abc"sv;86      const auto sep   = " "sv;87      [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep));88    }89  }90 91  return true;92}93 94constexpr bool test_string_literal_separator() {95  using namespace std::string_view_literals;96 97  // Splitting works as expected when the separator is a single character literal.98  {99    std::ranges::split_view v("abc def"sv, ' ');100    assert(is_equal(v, std::array{"abc"sv, "def"sv}));101  }102 103  // Counterintuitively, a seemingly equivalent separator expressed as a string literal doesn't match anything. This is104  // because of the implicit terminating null in the literal.105  {106    std::ranges::split_view v("abc def"sv, " ");107    assert(is_equal(v, std::array{"abc def"sv}));108  }109 110  // To illustrate the previous point further, the separator is actually a two-character string literal: `{' ', '\0'}`.111  // Should the input string contain that two-character sequence, the separator would match.112  {113    std::ranges::split_view v("abc \0def"sv, " ");114    assert(is_equal(v, std::array{"abc"sv, "def"sv}));115  }116 117  return true;118}119 120// Make sure that a string literal and a `string_view` produce the same results (which isn't always the case, see121// below).122template <class T>123constexpr std::string_view sv(T&& str) {124  return std::string_view(str);125};126 127template <class T, class Separator, class U, std::size_t M>128constexpr void test_one(T&& input, Separator&& separator, std::array<U, M> expected) {129  assert(test_function_call(input, separator, expected));130  assert(test_with_piping(input, separator, expected));131}132 133constexpr bool test_string_literals() {134  // These tests show characteristic examples of how using string literals with `split_view` produces unexpected135  // results due to the implicit terminating null that is treated as part of the range.136 137  using namespace std::string_view_literals;138 139  char short_sep = ' ';140  auto long_sep  = "12"sv;141 142  // When splitting a string literal, only the last segment will be null-terminated (getting the terminating null from143  // the original range).144  {145    std::array expected = {"abc"sv, std::string_view("def", sizeof("def"))};146 147    assert(test_function_call("abc def", short_sep, expected));148    assert(test_with_piping("abc def", short_sep, expected));149    assert(test_function_call("abc12def", long_sep, expected));150    assert(test_with_piping("abc12def", long_sep, expected));151  }152 153  // Empty string.154  {155    // Because an empty string literal contains an implicit terminating null, the output will contain one segment.156    std::array expected = {std::string_view("", 1)};157 158    assert(test_function_call("", short_sep, expected));159    assert(test_with_piping("", short_sep, expected));160    assert(test_function_call("", long_sep, expected));161    assert(test_with_piping("", long_sep, expected));162  }163 164  // Terminating null in the separator -- the character literal `' '` and the seemingly equivalent string literal `" "`165  // are treated differently due to the presence of an implicit `\0` in the latter.166  {167    const char input[]          = "abc def";168    std::array expected_unsplit = {std::string_view(input, sizeof(input))};169    std::array expected_split   = {"abc"sv, std::string_view("def", sizeof("def"))};170 171    assert(test_function_call(input, " ", expected_unsplit));172    assert(test_function_call("abc \0def", " ", expected_split));173    // Note: string literals don't work with piping because arrays decay to pointers, and pointers don't model `range`.174  }175 176  // Empty separator.177  {178    auto empty_sep      = ""sv;179    std::array expected = {"a"sv, "b"sv, "c"sv, "\0"sv};180 181    assert(test_function_call("abc", empty_sep, expected));182    assert(test_with_piping("abc", empty_sep, expected));183  }184 185  return true;186}187 188bool test_nontrivial_characters() {189  // Try a deliberately heavyweight "character" type to see if it triggers any corner cases.190 191  using Map = std::map<std::string, int>;192  using Vec = std::vector<Map>;193 194  Map sep = {{"yyy", 999}};195  Map m1  = {196      {"a", 1},197      {"bc", 2},198  };199  Map m2 = {200      {"def", 3},201  };202  Map m3 = {203      {"g", 4},204      {"hijk", 5},205  };206 207  Vec expected1 = {m1, m2};208  Vec expected2 = {m3};209 210  std::ranges::split_view v(Vec{m1, m2, sep, m3}, sep);211 212  // Segment 1: {m1, m2}213  auto outer = v.begin();214  assert(outer != v.end());215  auto inner = (*outer).begin();216  assert(*inner++ == m1);217  assert(*inner++ == m2);218  assert(inner == (*outer).end());219 220  // Segment 2: {m3}221  ++outer;222  assert(outer != v.end());223  inner = (*outer).begin();224  assert(*inner++ == m3);225  assert(inner == (*outer).end());226 227  ++outer;228  assert(outer == v.end());229 230  return true;231}232 233constexpr bool main_test() {234  using namespace std::string_view_literals;235 236  char short_sep = ' ';237  auto long_sep  = "12"sv;238 239  // One separator.240  {241    std::array expected = {"abc"sv, "def"sv};242    test_one("abc def"sv, short_sep, expected);243    test_one("abc12def"sv, long_sep, expected);244  }245 246  // Several separators in a row.247  {248    std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv};249    test_one("abc    def"sv, short_sep, expected);250    test_one("abc12121212def"sv, long_sep, expected);251  }252 253  // Trailing separator.254  {255    std::array expected = {"abc"sv, "def"sv, ""sv};256    test_one("abc def "sv, short_sep, expected);257    test_one("abc12def12"sv, long_sep, expected);258  }259 260  // Leading separator.261  {262    std::array expected = {""sv, "abc"sv, "def"sv};263    test_one(" abc def"sv, short_sep, expected);264    test_one("12abc12def"sv, long_sep, expected);265  }266 267  // No separator.268  {269    std::array expected = {"abc"sv};270    test_one("abc"sv, short_sep, expected);271    test_one("abc"sv, long_sep, expected);272  }273 274  // Input consisting of a single separator.275  {276    std::array expected = {""sv, ""sv};277    test_one(" "sv, short_sep, expected);278    test_one("12"sv, long_sep, expected);279  }280 281  // Input consisting of only separators.282  {283    std::array expected = {""sv, ""sv, ""sv, ""sv};284    test_one("   "sv, short_sep, expected);285    test_one("121212"sv, long_sep, expected);286  }287 288  // The separator and the string use the same character only.289  {290    auto overlapping_sep = "aaa"sv;291    std::array expected  = {""sv, "aa"sv};292    test_one("aaaaa"sv, overlapping_sep, expected);293  }294 295  // Many redundant separators.296  {297    std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv};298    test_one("  abc   def  "sv, short_sep, expected);299    test_one("1212abc121212def1212"sv, long_sep, expected);300  }301 302  // Separators after every character.303  {304    std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv};305    test_one(" a b c "sv, short_sep, expected);306    test_one("12a12b12c12"sv, long_sep, expected);307  }308 309  // Overlap between the separator and the string (see https://wg21.link/lwg3505).310  {311    auto overlapping_sep = "ab"sv;312    std::array expected  = {"a"sv, "aa"sv, ""sv, "b"sv};313    test_one("aabaaababb"sv, overlapping_sep, expected);314  }315 316  // Empty input.317  {318    std::array<std::string_view, 0> expected = {};319    test_one(""sv, short_sep, expected);320    test_one(""sv, long_sep, expected);321  }322 323  // Empty separator.324  {325    auto empty_sep      = ""sv;326    std::array expected = {"a"sv, "b"sv, "c"sv};327    test_one("abc"sv, empty_sep, expected);328    test_one("abc"sv, empty_sep, expected);329  }330 331  // Terminating null as a separator.332  {333    std::array expected = {"abc"sv, "def"sv};334    test_one("abc\0def"sv, '\0', expected);335    test_one("abc\0\0def"sv, "\0\0"sv, expected);336  }337 338  // Different character types.339  {340    // `char`.341    test_function_call("abc def", ' ', std::array{"abc"sv, "def"sv});342#ifndef TEST_HAS_NO_WIDE_CHARACTERS343    // `wchar_t`.344    test_function_call(L"abc def", L' ', std::array{L"abc"sv, L"def"sv});345#endif346    // `char8_t`.347    test_function_call(u8"abc def", u8' ', std::array{u8"abc"sv, u8"def"sv});348    // `char16_t`.349    test_function_call(u"abc def", u' ', std::array{u"abc"sv, u"def"sv});350    // `char32_t`.351    test_function_call(U"abc def", U' ', std::array{U"abc"sv, U"def"sv});352  }353 354  // Non-character input.355  {356    std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}};357    test_one(std::array{1, 2, 3, 0, 4, 5, 6}, 0, expected);358    test_one(std::array{1, 2, 3, 0, 0, 0, 4, 5, 6}, std::array{0, 0, 0}, expected);359  }360 361  return true;362}363 364constexpr bool example_test() {365  // example code in the spec366  std::string str{"the quick brown fox"};367  std::vector<std::string_view> result;368  for (auto r : std::views::split(str, ' ')) {369    result.emplace_back(r.begin(), r.end());370  }371  using namespace std::string_view_literals;372  auto expected = {"the"sv, "quick"sv, "brown"sv, "fox"sv};373  assert(std::ranges::equal(result, expected));374 375  return true;376}377 378int main(int, char**) {379  example_test();380  static_assert(example_test());381 382  test_string_literals();383  static_assert(test_string_literals());384 385  test_l_r_values();386  static_assert(test_l_r_values());387 388  test_string_literal_separator();389  static_assert(test_string_literal_separator());390 391  // Note: map is not `constexpr`, so this test is runtime-only.392  test_nontrivial_characters();393 394  return 0;395}396