280 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// template<bool OtherConst>12// requires sized_sentinel_for<zentinel<Const>, ziperator<OtherConst>>13// friend constexpr range_difference_t<maybe-const<OtherConst, InnerView>>14// operator-(const sentinel& x, const iterator<OtherConst>& y);15 16#include <cassert>17#include <concepts>18#include <functional>19#include <ranges>20#include <tuple>21 22#include "../types.h"23 24template <class Base = int*>25struct convertible_forward_sized_iterator {26 Base it_ = nullptr;27 28 using iterator_category = std::forward_iterator_tag;29 using value_type = int;30 using difference_type = std::intptr_t;31 32 convertible_forward_sized_iterator() = default;33 constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}34 35 template <std::convertible_to<Base> U>36 constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}37 38 constexpr decltype(*Base{}) operator*() const { return *it_; }39 40 constexpr convertible_forward_sized_iterator& operator++() {41 ++it_;42 return *this;43 }44 constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }45 46 friend constexpr bool47 operator==(const convertible_forward_sized_iterator&, const convertible_forward_sized_iterator&) = default;48 49 friend constexpr difference_type50 operator-(const convertible_forward_sized_iterator& x, const convertible_forward_sized_iterator& y) {51 return x.it_ - y.it_;52 }53};54static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);55 56template <class Base>57struct convertible_sized_sentinel {58 Base base_;59 explicit convertible_sized_sentinel() = default;60 constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}61 62 template <std::convertible_to<Base> U>63 constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}64 65 template <class U>66 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)67 friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {68 return s.base_ == base;69 }70 template <class U>71 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)72 friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {73 return s.base_ - i;74 }75 76 template <class U>77 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)78 friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {79 return i - s.base_;80 }81};82static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,83 convertible_forward_sized_iterator<>>);84static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,85 convertible_forward_sized_iterator<int*>>);86static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,87 convertible_forward_sized_iterator<const int*>>);88 89struct ConstCompatibleForwardSized : IntBufferView {90 using IntBufferView::IntBufferView;91 92 using iterator = convertible_forward_sized_iterator<int*>;93 using const_iterator = convertible_forward_sized_iterator<const int*>;94 95 constexpr iterator begin() { return {buffer_}; }96 constexpr const_iterator begin() const { return {buffer_}; }97 constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }98 constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }99};100 101template <class T, class U>102concept HasMinus = std::invocable<std::minus<>, const T&, const U&>;103 104template <class T>105concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;106 107constexpr bool test() {108 int buffer1[5] = {1, 2, 3, 4, 5};109 int buffer2[3] = {1, 2, 3};110 111 {112 // shortest range113 std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0, 3), ForwardSizedNonCommon(buffer1));114 static_assert(!std::ranges::common_range<decltype(v)>);115 auto it = v.begin();116 auto st = v.end();117 assert(st - it == 3);118 assert(st - std::ranges::next(it, 1) == 2);119 120 assert(it - st == -3);121 assert(std::ranges::next(it, 1) - st == -2);122 static_assert(SentinelHasMinus<decltype(v)>);123 }124 125 {126 // underlying sentinel does not model sized_sentinel_for127 std::ranges::zip_transform_view v(MakeTuple{}, std::views::iota(0), SizedRandomAccessView(buffer1));128 static_assert(!std::ranges::common_range<decltype(v)>);129 static_assert(!SentinelHasMinus<decltype(v)>);130 }131 132 {133 // const incompatible:134 // underlying const sentinels cannot subtract underlying iterators135 // underlying sentinels cannot subtract underlying const iterators136 std::ranges::zip_transform_view v(MakeTuple{}, NonSimpleForwardSizedNonCommon{buffer1});137 static_assert(!std::ranges::common_range<decltype(v)>);138 static_assert(!simple_view<decltype(v)>);139 140 using Iter = std::ranges::iterator_t<decltype(v)>;141 using ConstIter = std::ranges::iterator_t<const decltype(v)>;142 static_assert(!std::is_same_v<Iter, ConstIter>);143 using Sentinel = std::ranges::sentinel_t<decltype(v)>;144 using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;145 static_assert(!std::is_same_v<Sentinel, ConstSentinel>);146 147 static_assert(HasMinus<Iter, Sentinel>);148 static_assert(HasMinus<Sentinel, Iter>);149 static_assert(HasMinus<ConstIter, ConstSentinel>);150 static_assert(HasMinus<ConstSentinel, ConstIter>);151 auto it = v.begin();152 auto const_it = std::as_const(v).begin();153 auto st = v.end();154 auto const_st = std::as_const(v).end();155 assert(it - st == -5);156 assert(st - it == 5);157 assert(const_it - const_st == -5);158 assert(const_st - const_it == 5);159 160 static_assert(!HasMinus<Iter, ConstSentinel>);161 static_assert(!HasMinus<ConstSentinel, Iter>);162 static_assert(!HasMinus<ConstIter, Sentinel>);163 static_assert(!HasMinus<Sentinel, ConstIter>);164 }165 166 {167 // const compatible allow non-const to const conversion168 std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1});169 static_assert(!std::ranges::common_range<decltype(v)>);170 static_assert(!simple_view<decltype(v)>);171 172 using Iter = std::ranges::iterator_t<decltype(v)>;173 using ConstIter = std::ranges::iterator_t<const decltype(v)>;174 static_assert(!std::is_same_v<Iter, ConstIter>);175 using Sentinel = std::ranges::sentinel_t<decltype(v)>;176 using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;177 static_assert(!std::is_same_v<Sentinel, ConstSentinel>);178 179 static_assert(HasMinus<Iter, Sentinel>);180 static_assert(HasMinus<Sentinel, Iter>);181 static_assert(HasMinus<ConstIter, ConstSentinel>);182 static_assert(HasMinus<ConstSentinel, ConstIter>);183 static_assert(HasMinus<Iter, ConstSentinel>);184 static_assert(HasMinus<ConstSentinel, Iter>);185 static_assert(HasMinus<ConstIter, Sentinel>);186 static_assert(HasMinus<Sentinel, ConstIter>);187 188 auto it = v.begin();189 auto const_it = std::as_const(v).begin();190 auto st = v.end();191 auto const_st = std::as_const(v).end();192 193 assert(it - st == -5);194 assert(st - it == 5);195 assert(const_it - const_st == -5);196 assert(const_st - const_it == 5);197 assert(it - const_st == -5);198 assert(const_st - it == 5);199 assert(const_it - st == -5);200 assert(st - const_it == 5);201 }202 203 auto testMinus = [](auto&& v, auto distance) {204 auto it = v.begin();205 auto const_it = std::as_const(v).begin();206 auto st = v.end();207 auto const_st = std::as_const(v).end();208 209 assert(it - st == -distance);210 assert(st - it == distance);211 assert(const_it - const_st == -distance);212 assert(const_st - const_it == distance);213 assert(it - const_st == -distance);214 assert(const_st - it == distance);215 assert(const_it - st == -distance);216 assert(st - const_it == distance);217 };218 219 {220 // one range221 std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized{buffer1});222 testMinus(v, 5);223 }224 225 {226 // two ranges227 std::ranges::zip_transform_view v(GetFirst{}, ConstCompatibleForwardSized{buffer1}, std::views::iota(0, 100));228 testMinus(v, 5);229 }230 231 {232 // three ranges233 std::ranges::zip_transform_view v(234 Tie{},235 ConstCompatibleForwardSized{buffer1},236 ConstCompatibleForwardSized{buffer2},237 std::ranges::single_view(2.));238 testMinus(v, 1);239 }240 241 {242 // single empty range243 std::ranges::zip_transform_view v(MakeTuple{}, ConstCompatibleForwardSized(nullptr, 0));244 testMinus(v, 0);245 }246 247 {248 // empty range at the beginning249 std::ranges::zip_transform_view v(250 MakeTuple{}, std::ranges::empty_view<int>(), ConstCompatibleForwardSized{buffer1}, SimpleCommon{buffer2});251 testMinus(v, 0);252 }253 254 {255 // empty range in the middle256 std::ranges::zip_transform_view v(257 MakeTuple{},258 ConstCompatibleForwardSized{buffer1},259 std::ranges::empty_view<int>(),260 ConstCompatibleForwardSized{buffer2});261 testMinus(v, 0);262 }263 264 {265 // empty range at the end266 std::ranges::zip_transform_view v(267 MakeTuple{}, SimpleCommon{buffer1}, ConstCompatibleForwardSized{buffer2}, std::ranges::empty_view<int>());268 testMinus(v, 0);269 }270 271 return true;272}273 274int main(int, char**) {275 test();276 static_assert(test());277 278 return 0;279}280