xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.elements/sentinel/minus.pass.cpp (revision 94461822c75d5080bf648f86552f7a59b76905c9)
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // template<bool OtherConst>
12 //   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
13 // friend constexpr range_difference_t<maybe-const<OtherConst, V>>
14 //   operator-(const iterator<OtherConst>& x, const sentinel& y);
15 //
16 // template<bool OtherConst>
17 //   requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
18 // friend constexpr range_difference_t<maybe-const<OtherConst, V>>
19 //   operator-(const sentinel& x, const iterator<OtherConst>& y);
20 
21 #include <cassert>
22 #include <concepts>
23 #include <functional>
24 #include <ranges>
25 #include <tuple>
26 
27 #include "../types.h"
28 
29 template <bool Const>
30 struct Iter {
31   std::tuple<int>* it_;
32 
33   using value_type       = std::tuple<int>;
34   using difference_type  = intptr_t;
35   using iterator_concept = std::input_iterator_tag;
36 
37   constexpr decltype(auto) operator*() const { return *it_; }
38   constexpr Iter& operator++() {
39     ++it_;
40     return *this;
41   }
42   constexpr void operator++(int) { ++it_; }
43 };
44 
45 template <bool Const>
46 struct Sent {
47   std::tuple<int>* end_;
48 
49   constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
50 };
51 
52 template <bool Const>
53 struct SizedSent {
54   std::tuple<int>* end_;
55 
56   constexpr bool operator==(const Iter<Const>& i) const { return i.it_ == end_; }
57 
58   friend constexpr auto operator-(const SizedSent& st, const Iter<Const>& it) { return st.end_ - it.it_; }
59 
60   friend constexpr auto operator-(const Iter<Const>& it, const SizedSent& st) { return it.it_ - st.end_; }
61 };
62 
63 template <bool Const>
64 struct CrossSizedSent {
65   std::tuple<int>* end_;
66 
67   template <bool C>
68   constexpr bool operator==(const Iter<C>& i) const {
69     return i.it_ == end_;
70   }
71 
72   template <bool C>
73   friend constexpr auto operator-(const CrossSizedSent& st, const Iter<C>& it) {
74     return st.end_ - it.it_;
75   }
76 
77   template <bool C>
78   friend constexpr auto operator-(const Iter<C>& it, const CrossSizedSent& st) {
79     return it.it_ - st.end_;
80   }
81 };
82 
83 template <template <bool> class It, template <bool> class St>
84 struct Range : TupleBufferView {
85   using TupleBufferView::TupleBufferView;
86 
87   using iterator       = It<false>;
88   using sentinel       = St<false>;
89   using const_iterator = It<true>;
90   using const_sentinel = St<true>;
91 
92   constexpr iterator begin() { return {buffer_}; }
93   constexpr const_iterator begin() const { return {buffer_}; }
94   constexpr sentinel end() { return sentinel{buffer_ + size_}; }
95   constexpr const_sentinel end() const { return const_sentinel{buffer_ + size_}; }
96 };
97 
98 template <class T, class U>
99 concept HasMinus = requires(const T t, const U u) { t - u; };
100 
101 template <class BaseRange>
102 using ElementsView = std::ranges::elements_view<BaseRange, 0>;
103 
104 template <class BaseRange>
105 using ElemIter = std::ranges::iterator_t<ElementsView<BaseRange>>;
106 
107 template <class BaseRange>
108 using EleConstIter = std::ranges::iterator_t<const ElementsView<BaseRange>>;
109 
110 template <class BaseRange>
111 using EleSent = std::ranges::sentinel_t<ElementsView<BaseRange>>;
112 
113 template <class BaseRange>
114 using EleConstSent = std::ranges::sentinel_t<const ElementsView<BaseRange>>;
115 
116 constexpr void testConstraints() {
117   // base is not sized
118   {
119     using Base = Range<Iter, Sent>;
120     static_assert(!HasMinus<EleSent<Base>, ElemIter<Base>>);
121     static_assert(!HasMinus<ElemIter<Base>, EleSent<Base>>);
122 
123     static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>);
124     static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>);
125 
126     static_assert(!HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
127     static_assert(!HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
128 
129     static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>);
130     static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>);
131   }
132 
133   // base is sized but not cross const
134   {
135     using Base = Range<Iter, SizedSent>;
136     static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>);
137     static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>);
138 
139     static_assert(!HasMinus<EleSent<Base>, EleConstIter<Base>>);
140     static_assert(!HasMinus<EleConstIter<Base>, EleSent<Base>>);
141 
142     static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
143     static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
144 
145     static_assert(!HasMinus<EleConstSent<Base>, ElemIter<Base>>);
146     static_assert(!HasMinus<ElemIter<Base>, EleConstSent<Base>>);
147   }
148 
149   // base is cross const sized
150   {
151     using Base = Range<Iter, CrossSizedSent>;
152     static_assert(HasMinus<EleSent<Base>, ElemIter<Base>>);
153     static_assert(HasMinus<ElemIter<Base>, EleSent<Base>>);
154 
155     static_assert(HasMinus<EleSent<Base>, EleConstIter<Base>>);
156     static_assert(HasMinus<EleConstIter<Base>, EleSent<Base>>);
157 
158     static_assert(HasMinus<EleConstSent<Base>, EleConstIter<Base>>);
159     static_assert(HasMinus<EleConstIter<Base>, EleConstSent<Base>>);
160 
161     static_assert(HasMinus<EleConstSent<Base>, ElemIter<Base>>);
162     static_assert(HasMinus<ElemIter<Base>, EleConstSent<Base>>);
163   }
164 }
165 
166 constexpr bool test() {
167   std::tuple<int> buffer[] = {{1}, {2}, {3}, {4}, {5}};
168 
169   // base is sized but not cross const
170   {
171     using Base = Range<Iter, SizedSent>;
172     Base base{buffer};
173     auto ev         = base | std::views::elements<0>;
174     auto iter       = ev.begin();
175     auto const_iter = std::as_const(ev).begin();
176     auto sent       = ev.end();
177     auto const_sent = std::as_const(ev).end();
178 
179     assert(iter - sent == -5);
180     assert(sent - iter == 5);
181     assert(const_iter - const_sent == -5);
182     assert(const_sent - const_iter == 5);
183   }
184 
185   // base is cross const sized
186   {
187     using Base = Range<Iter, CrossSizedSent>;
188     Base base{buffer};
189     auto ev         = base | std::views::elements<0>;
190     auto iter       = ev.begin();
191     auto const_iter = std::as_const(ev).begin();
192     auto sent       = ev.end();
193     auto const_sent = std::as_const(ev).end();
194 
195     assert(iter - sent == -5);
196     assert(sent - iter == 5);
197     assert(iter - const_sent == -5);
198     assert(const_sent - iter == 5);
199     assert(const_iter - sent == -5);
200     assert(sent - const_iter == 5);
201     assert(const_iter - const_sent == -5);
202     assert(const_sent - const_iter == 5);
203   }
204 
205   return true;
206 }
207 
208 int main(int, char**) {
209   test();
210   static_assert(test());
211 
212   return 0;
213 }
214