222 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 30template <class T>31concept HasPreDecrement = requires(T t) {32 { --t };33};34 35template <class T>36concept HasPostDecrement = requires(T t) {37 { t-- };38};39 40struct TrackingPred : TrackInitialization {41 using TrackInitialization::TrackInitialization;42 constexpr bool operator()(int x, int y) const { return x <= y; }43};44 45template <class Iter, IsConst Constant, class Sent = sentinel_wrapper<Iter>>46constexpr void test() {47 using Underlying = View<Iter, Sent>;48 using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;49 using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;50 51 static_assert(HasPostDecrement<ChunkByIterator>);52 static_assert(HasPreDecrement<ChunkByIterator>);53 54 auto make_chunk_by_view = [](auto& arr) {55 View view{Iter{arr.data()}, Sent{Iter{arr.data() + arr.size()}}};56 return ChunkByView{std::move(view), std::ranges::less_equal{}};57 };58 59 // Test with a single chunk60 {61 std::array array{0, 1, 2, 3, 4};62 ChunkByView view = make_chunk_by_view(array);63 ChunkByIterator it = std::ranges::next(view.begin(), view.end());64 65 std::same_as<ChunkByIterator&> decltype(auto) result = --it;66 assert(&result == &it);67 assert(base((*result).begin()) == array.data());68 }69 70 // Test with two chunks71 {72 std::array array{0, 1, 2, 0, 1, 2};73 ChunkByView view = make_chunk_by_view(array);74 ChunkByIterator it = std::ranges::next(view.begin(), view.end());75 76 std::same_as<ChunkByIterator&> decltype(auto) result = --it;77 assert(&result == &it);78 assert(base((*result).begin()) == array.data() + 3);79 80 --it;81 assert(base((*result).begin()) == array.data());82 }83 84 // Test going forward and then backward on the same iterator85 {86 std::array array{7, 8, 9, 4, 5, 6, 1, 2, 3, 0};87 ChunkByView view = make_chunk_by_view(array);88 ChunkByIterator it = view.begin();89 90 ++it;91 --it;92 assert(base((*it).begin()) == array.data());93 assert(base((*it).end()) == array.data() + 3);94 95 ++it;96 ++it;97 --it;98 assert(base((*it).begin()) == array.data() + 3);99 assert(base((*it).end()) == array.data() + 6);100 101 ++it;102 ++it;103 --it;104 assert(base((*it).begin()) == array.data() + 6);105 assert(base((*it).end()) == array.data() + 9);106 107 ++it;108 ++it;109 --it;110 assert(base((*it).begin()) == array.data() + 9);111 }112 113 // Decrement an iterator multiple times114 if constexpr (std::ranges::common_range<Underlying>) {115 std::array array{1, 2, 1, 2, 1};116 ChunkByView view = make_chunk_by_view(array);117 118 ChunkByIterator it = view.end();119 --it;120 --it;121 --it;122 assert(base((*it).begin()) == array.data());123 }124 125 // Test with a predicate that takes by non-const reference126 if constexpr (!std::to_underlying(Constant)) {127 std::array array{1, 2, 3, -3, -2, -1};128 View v{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}};129 auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });130 131 auto it = std::ranges::next(view.begin());132 assert(base((*it).begin()) == array.data() + 3);133 --it;134 assert(base((*it).begin()) == array.data());135 }136 137 // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')138 {139 std::array array = {1, 2, 3, -3, -2, -1};140 auto v = View{Iter{array.data()}, Sent{Iter{array.data() + array.size()}}} |141 std::views::transform([](int x) { return IntWrapper{x}; });142 auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);143 144 auto it = std::ranges::next(view.begin());145 assert(base((*it).begin().base()) == array.data() + 3);146 --it;147 assert(base((*it).begin().base()) == array.data());148 }149 150 // Make sure we do not make a copy of the predicate when we decrement151 if constexpr (std::ranges::common_range<Underlying>) {152 bool moved = false, copied = false;153 std::array array{1, 2, 1, 3};154 View v{Iter(array.data()), Sent(Iter(array.data() + array.size()))};155 auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));156 assert(std::exchange(moved, false));157 auto it = view.end();158 --it;159 it--;160 assert(!moved);161 assert(!copied);162 }163 164 // Check post-decrement165 {166 std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};167 ChunkByView view = make_chunk_by_view(array);168 ChunkByIterator it = std::ranges::next(view.begin(), view.end());169 170 std::same_as<ChunkByIterator> decltype(auto) result = it--;171 assert(result != it);172 assert(result == std::default_sentinel);173 assert(base((*it).begin()) == array.data() + 6);174 175 result = it--;176 assert(base((*it).begin()) == array.data() + 3);177 assert(base((*result).begin()) == array.data() + 6);178 179 result = it--;180 assert(base((*it).begin()) == array.data());181 assert(base((*result).begin()) == array.data() + 3);182 }183}184 185template <class Iterator, IsConst Constant>186constexpr void test_with_pair() {187 // Test with pair of iterators188 test<Iterator, Constant>();189 190 // Test with iterator-sentinel pair191 test<Iterator, Constant, Iterator>();192}193 194constexpr bool tests() {195 test_with_pair<bidirectional_iterator<int*>, IsConst::no>();196 test_with_pair<random_access_iterator<int*>, IsConst::no>();197 test_with_pair<contiguous_iterator<int*>, IsConst::no>();198 test_with_pair<int*, IsConst::no>();199 200 test_with_pair<bidirectional_iterator<int const*>, IsConst::yes>();201 test_with_pair<random_access_iterator<int const*>, IsConst::yes>();202 test_with_pair<contiguous_iterator<int const*>, IsConst::yes>();203 test_with_pair<int const*, IsConst::yes>();204 205 // Make sure `operator--` isn't provided for non bidirectional ranges206 {207 using ForwardView = View<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>;208 using ChunkByView = std::ranges::chunk_by_view<ForwardView, std::ranges::less_equal>;209 static_assert(!HasPreDecrement<std::ranges::iterator_t<ChunkByView>>);210 static_assert(!HasPostDecrement<std::ranges::iterator_t<ChunkByView>>);211 }212 213 return true;214}215 216int main(int, char**) {217 tests();218 static_assert(tests());219 220 return 0;221}222