180 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++17, c++2010 11// <ranges>12 13// constexpr iterator& operator++();14// constexpr iterator operator++(int);15 16#include <ranges>17 18#include <array>19#include <cassert>20#include <concepts>21#include <functional>22#include <span>23#include <type_traits>24#include <utility>25 26#include "../types.h"27#include "test_iterators.h"28#include "test_macros.h"29 30struct TrackingPred : TrackInitialization {31 using TrackInitialization::TrackInitialization;32 constexpr bool operator()(int x, int y) const { return x <= y; }33};34 35template <class Iterator, IsConst Constant>36constexpr void test() {37 using Sentinel = sentinel_wrapper<Iterator>;38 using Underlying = View<Iterator, Sentinel>;39 using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;40 using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;41 42 auto make_chunk_by_view = [](auto& arr) {43 View view{Iterator{arr.data()}, Sentinel{Iterator{arr.data() + arr.size()}}};44 return ChunkByView{std::move(view), std::ranges::less_equal{}};45 };46 47 // Increment the iterator when it won't find another satisfied value after begin()48 {49 std::array array{0, 1, 2, 3, 4};50 ChunkByView view = make_chunk_by_view(array);51 ChunkByIterator it = view.begin();52 53 std::same_as<ChunkByIterator&> decltype(auto) result = ++it;54 assert(&result == &it);55 assert(result == view.end());56 assert(result == std::default_sentinel);57 }58 59 // Increment the iterator and it finds another value after begin()60 {61 std::array array{1, 2, 3, -1, -2, -3};62 int const* second_chunk = array.data() + 3;63 ChunkByView view = make_chunk_by_view(array);64 65 ChunkByIterator it = view.begin();66 ++it;67 assert(base((*it).begin()) == second_chunk);68 }69 70 // Increment advances all the way to the end of the range71 {72 std::array array{1, 2, 3, 4, 1};73 ChunkByView view = make_chunk_by_view(array);74 75 ChunkByIterator it = view.begin();76 ++it;77 assert(base((*it).begin()) == array.data() + 4);78 }79 80 // Increment an iterator multiple times81 {82 std::array array{0, 1, 0, 2, 0, 3, 0, 4};83 ChunkByView view = make_chunk_by_view(array);84 85 ChunkByIterator it = view.begin();86 assert(base((*it).begin()) == array.data());87 ++it;88 assert(base((*it).begin()) == array.data() + 2);89 ++it;90 assert(base((*it).begin()) == array.data() + 4);91 ++it;92 assert(base((*it).begin()) == array.data() + 6);93 ++it;94 assert(it == std::default_sentinel);95 }96 97 // Test with a predicate that takes by non-const reference98 if constexpr (!std::to_underlying(Constant)) {99 std::array array{1, 2, 3, -3, -2, -1};100 View v{Iterator{array.data()}, Sentinel{Iterator{array.data() + array.size()}}};101 auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });102 103 auto it = view.begin();104 assert(base((*it).begin()) == array.data());105 ++it;106 assert(base((*it).begin()) == array.data() + 3);107 }108 109 // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')110 {111 std::array array = {1, 2, 3, -3, -2, -1};112 auto v = View{Iterator{array.data()}, Sentinel{Iterator{array.data() + array.size()}}} |113 std::views::transform([](int x) { return IntWrapper{x}; });114 auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);115 116 auto it = view.begin();117 assert(base((*it).begin().base()) == array.data());118 ++it;119 assert(base((*it).begin().base()) == array.data() + 3);120 }121 122 // Make sure we do not make a copy of the predicate when we increment123 // (we should be passing it to ranges::adjacent_find using std::ref)124 {125 bool moved = false, copied = false;126 std::array array{1, 2, 1, 3};127 View v{Iterator(array.data()), Sentinel(Iterator(array.data() + array.size()))};128 auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));129 assert(std::exchange(moved, false));130 auto it = view.begin();131 ++it;132 it++;133 assert(!moved);134 assert(!copied);135 }136 137 // Check post-increment138 {139 std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};140 ChunkByView view = make_chunk_by_view(array);141 ChunkByIterator it = view.begin();142 143 std::same_as<ChunkByIterator> decltype(auto) result = it++;144 assert(result != it);145 assert(base((*result).begin()) == array.data());146 assert(base((*it).begin()) == array.data() + 3);147 148 result = it++;149 assert(base((*result).begin()) == array.data() + 3);150 assert(base((*it).begin()) == array.data() + 6);151 152 result = it++;153 assert(base((*result).begin()) == array.data() + 6);154 assert(it == std::default_sentinel);155 }156}157 158constexpr bool tests() {159 test<forward_iterator<int*>, IsConst::no>();160 test<bidirectional_iterator<int*>, IsConst::no>();161 test<random_access_iterator<int*>, IsConst::no>();162 test<contiguous_iterator<int*>, IsConst::no>();163 test<int*, IsConst::no>();164 165 test<forward_iterator<int const*>, IsConst::yes>();166 test<bidirectional_iterator<int const*>, IsConst::yes>();167 test<random_access_iterator<int const*>, IsConst::yes>();168 test<contiguous_iterator<int const*>, IsConst::yes>();169 test<int const*, IsConst::yes>();170 171 return true;172}173 174int main(int, char**) {175 tests();176 static_assert(tests());177 178 return 0;179}180