|
18 | 18 | #include "double_move_tracker.h" |
19 | 19 | #include "test_iterators.h" |
20 | 20 |
|
21 | | -int globalArray[8] = {0, 1, 2, 3, 4, 5, 6, 7}; |
22 | | -bool already_moved = false; |
| 21 | +int val[] = {1, 2, 3}; |
23 | 22 |
|
24 | | -template <class It, class ItTraits = It > |
25 | | -class ThrowOnMoveIterator { |
26 | | - using Traits = std::iterator_traits<ItTraits>; |
27 | | - It it_; |
28 | | - support::double_move_tracker tracker_; |
| 23 | +bool flag = false; |
29 | 24 |
|
30 | | - template <class U, class T> |
31 | | - friend class ThrowOnMoveIterator; |
| 25 | +// Forward declaration |
| 26 | +template<std::size_t N> struct Iter; |
32 | 27 |
|
33 | | -public: |
34 | | - using iterator_category = std::input_iterator_tag; |
35 | | - using value_type = typename Traits::value_type; |
36 | | - using difference_type = typename Traits::difference_type; |
37 | | - using pointer = It; |
38 | | - using reference = typename Traits::reference; |
| 28 | +// Iterator class |
| 29 | +template<std::size_t N> |
| 30 | +struct Iter { |
| 31 | + using value_type = int; |
| 32 | + using difference_type = std::ptrdiff_t; |
| 33 | + using reference = int&; |
| 34 | + using pointer = int*; |
| 35 | + using iterator_category = std::random_access_iterator_tag; |
| 36 | + using iterator_concept = std::random_access_iterator_tag; |
39 | 37 |
|
40 | | - constexpr ThrowOnMoveIterator() {} |
| 38 | +private: |
| 39 | + int* ptr_ = nullptr; |
41 | 40 |
|
42 | | - constexpr ThrowOnMoveIterator(It it) : it_(it) {} |
| 41 | + // Allow cross-template access to ptr_ |
| 42 | + template<std::size_t M> friend struct Iter; |
43 | 43 |
|
44 | | - template <class U, class T> |
45 | | - constexpr ThrowOnMoveIterator(const ThrowOnMoveIterator<U, T>& u) : it_(u.it_), tracker_(u.tracker_) {} |
| 44 | +public: |
| 45 | + // Constructors |
| 46 | + Iter() = default; |
| 47 | + Iter(int* ptr) : ptr_(ptr) {} |
| 48 | + Iter(const Iter&) = default; |
| 49 | + Iter(Iter&& other) noexcept : ptr_(other.ptr_) {} |
| 50 | + |
| 51 | + // Cross-template constructor |
| 52 | + template<std::size_t M> |
| 53 | + Iter(const Iter<M>& other) : ptr_(other.ptr_) {} |
| 54 | + |
| 55 | + // Assignment operators |
| 56 | + Iter& operator=(const Iter&) = default; |
| 57 | + Iter& operator=(Iter&& other) noexcept { |
| 58 | + ptr_ = other.ptr_; |
| 59 | + return *this; |
| 60 | + } |
46 | 61 |
|
47 | | - constexpr ThrowOnMoveIterator(const ThrowOnMoveIterator&) {} |
| 62 | + // Dereference and access |
| 63 | + reference operator*() const { return *ptr_; } |
| 64 | + pointer operator->() const { return ptr_; } |
| 65 | + reference operator[](difference_type n) const { return ptr_[n]; } |
48 | 66 |
|
49 | | - constexpr ThrowOnMoveIterator(ThrowOnMoveIterator&&) { |
50 | | - std::cout << "moved ctor called with: " << already_moved << std::endl; |
51 | | - if (!already_moved) { |
52 | | - already_moved = true; |
53 | | - throw std::runtime_error("Move failed in iter"); |
54 | | - } |
55 | | - } |
| 67 | + // Increment and decrement |
| 68 | + Iter& operator++() { ++ptr_; return *this; } |
| 69 | + Iter operator++(int) { auto tmp = *this; ++*this; return tmp; } |
| 70 | + Iter& operator--() { --ptr_; return *this; } |
| 71 | + Iter operator--(int) { auto tmp = *this; --*this; return tmp; } |
56 | 72 |
|
57 | | - ThrowOnMoveIterator& operator=(ThrowOnMoveIterator&&) { |
58 | | - if (!already_moved) { |
59 | | - already_moved = true; |
60 | | - throw std::runtime_error("Move assignment failed in iter"); |
61 | | - } |
62 | | - return *this; |
63 | | - } |
| 73 | + // Arithmetic operations |
| 74 | + Iter& operator+=(difference_type n) { ptr_ += n; return *this; } |
| 75 | + Iter& operator-=(difference_type n) { ptr_ -= n; return *this; } |
64 | 76 |
|
65 | | - constexpr reference operator*() const { return *it_; } |
| 77 | + // Declare friend operators (no inline definition) |
| 78 | + template<std::size_t X> |
| 79 | + friend Iter<X> operator+(Iter<X> it, difference_type n); |
66 | 80 |
|
67 | | - constexpr ThrowOnMoveIterator& operator++() { |
68 | | - ++it_; |
69 | | - return *this; |
70 | | - } |
71 | | - constexpr ThrowOnMoveIterator operator++(int) { return ThrowOnMoveIterator(it_++); } |
| 81 | + template<std::size_t X> |
| 82 | + friend Iter<X> operator+(difference_type n, Iter<X> it); |
72 | 83 |
|
73 | | - friend constexpr bool operator==(const ThrowOnMoveIterator& x, const ThrowOnMoveIterator& y) { |
74 | | - return x.it_ == y.it_; |
75 | | - } |
76 | | - friend constexpr bool operator!=(const ThrowOnMoveIterator& x, const ThrowOnMoveIterator& y) { |
77 | | - return x.it_ != y.it_; |
78 | | - } |
| 84 | + template<std::size_t X> |
| 85 | + friend Iter<X> operator-(Iter<X> it, difference_type n); |
79 | 86 |
|
80 | | - friend constexpr It base(const ThrowOnMoveIterator& i) { return i.it_; } |
| 87 | + template<std::size_t X, std::size_t Y> |
| 88 | + friend difference_type operator-(Iter<X> a, Iter<Y> b); |
81 | 89 |
|
82 | | - template <class T> |
83 | | - void operator,(T const&) = delete; |
| 90 | + // Comparison operators |
| 91 | + friend bool operator==(Iter a, Iter b) = default; |
| 92 | + friend bool operator<(Iter a, Iter b) { return a.ptr_ < b.ptr_; } |
| 93 | + friend bool operator>(Iter a, Iter b) { return a.ptr_ > b.ptr_; } |
| 94 | + friend bool operator<=(Iter a, Iter b) { return a.ptr_ <= b.ptr_; } |
| 95 | + friend bool operator>=(Iter a, Iter b) { return a.ptr_ >= b.ptr_; } |
84 | 96 | }; |
85 | 97 |
|
86 | | -template <class T> |
87 | | -struct BufferView : std::ranges::view_base { |
88 | | - T* buffer_; |
89 | | - std::size_t size_; |
| 98 | +// Define operators outside the class |
| 99 | +template<std::size_t X> |
| 100 | +inline Iter<X> operator+(Iter<X> it, std::ptrdiff_t n) { |
| 101 | + return Iter<X>(it.ptr_ + n); |
| 102 | +} |
90 | 103 |
|
91 | | - template <std::size_t N> |
92 | | - constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {} |
93 | | -}; |
| 104 | +template<std::size_t X> |
| 105 | +inline Iter<X> operator+(std::ptrdiff_t n, Iter<X> it) { |
| 106 | + return Iter<X>(it.ptr_ + n); |
| 107 | +} |
94 | 108 |
|
95 | | -using IntBufferView = BufferView<int>; |
| 109 | +template<std::size_t X> |
| 110 | +inline Iter<X> operator-(Iter<X> it, std::ptrdiff_t n) { |
| 111 | + return Iter<X>(it.ptr_ - n); |
| 112 | +} |
96 | 113 |
|
97 | | -template <bool Simple> |
98 | | -struct Common : IntBufferView { |
99 | | - using IntBufferView::IntBufferView; |
100 | | - using Iter = ThrowOnMoveIterator<int*>; |
101 | | - using ConstIter = ThrowOnMoveIterator<const int*>; |
102 | | - using Sent = sentinel_wrapper<Iter>; |
103 | | - using ConstSent = sentinel_wrapper<ConstIter>; |
| 114 | +template<std::size_t X, std::size_t Y> |
| 115 | +inline std::ptrdiff_t operator-(Iter<X> a, Iter<Y> b) { |
| 116 | + return a.ptr_ - b.ptr_; |
| 117 | +} |
104 | 118 |
|
105 | | - constexpr Iter begin() |
106 | | - requires(!Simple) |
107 | | - { |
108 | | - return Iter{buffer_}; |
109 | | - } |
110 | | - constexpr const ConstIter begin() const { return ConstIter{buffer_}; } |
111 | | - constexpr Sent end() |
112 | | - requires(!Simple) |
113 | | - { |
114 | | - return Sent(buffer_ + size_); |
115 | | - } |
116 | | - constexpr ConstSent end() const { return ConstSent(buffer_ + size_); } |
| 119 | +template<std::size_t N> |
| 120 | +struct Range : std::ranges::view_base { |
| 121 | + using iterator = Iter<N>; |
| 122 | + using const_iterator = Iter<N>; |
| 123 | + |
| 124 | + int* data_; |
| 125 | + std::size_t size_; |
| 126 | + |
| 127 | + Range() : data_(val), size_(4) {} |
| 128 | + |
| 129 | + Range(int* data, std::size_t size) : data_(data), size_(size) {} |
| 130 | + |
| 131 | + iterator begin() { return iterator(data_); } |
| 132 | + iterator end() { return iterator(data_ + size_); } |
| 133 | + |
| 134 | + const_iterator begin() const { return const_iterator(data_); } |
| 135 | + const_iterator end() const { return const_iterator(data_ + size_); } |
| 136 | + |
| 137 | + std::size_t size() const { return size_; } |
117 | 138 | }; |
118 | 139 |
|
119 | | -using SimpleCommon = Common<true>; |
120 | | -using NonSimpleCommon = Common<false>; |
| 140 | +static_assert(std::ranges::range<Range<0>>); |
| 141 | +static_assert(std::ranges::sized_range<Range<0>>); |
121 | 142 |
|
122 | 143 | int main() { |
| 144 | + |
123 | 145 | { |
124 | | - int buffer[3] = {1, 2, 3}; |
125 | | - int buffer2[3] = {1, 2, 3}; |
126 | | - NonSimpleCommon view(buffer); |
127 | | - NonSimpleCommon view2(buffer2); |
128 | | - std::ranges::concat_view v(view, view2); |
129 | | - std::ranges::iterator_t<std::ranges::concat_view<decltype(view), decltype(view2)>> it1; |
| 146 | + //valueless by exception test operator* |
| 147 | + Range<0> r1; |
| 148 | + Range<1> r2; |
| 149 | + |
| 150 | + auto cv = std::views::concat(r1, r2); |
| 151 | + auto iter1 = cv.begin(); |
| 152 | + auto iter2 = std::ranges::next(cv.begin(), 4); |
| 153 | + flag = true; |
130 | 154 | try { |
131 | | - it1 = v.begin(); |
| 155 | + iter1 = std::move(iter2); |
132 | 156 | } catch (...) { |
133 | | - std::cout << "hit catch" << std::endl; |
134 | | - //ASSERT_SAME_TYPE(std::ranges::iterator_t<const decltype(v)>, int); |
135 | | - std::ranges::iterator_t<const decltype(v)> it2(it1); |
| 157 | + auto f = std::ranges::distance(r1); |
| 158 | + (void)f; |
136 | 159 | TEST_LIBCPP_ASSERT_FAILURE( |
137 | 160 | [=] { |
138 | | - std::ranges::iterator_t<const decltype(v)> it2(it1); |
139 | | - (void)it2; |
| 161 | + *iter1; |
140 | 162 | }(), |
141 | 163 | "valueless by exception"); |
142 | 164 | } |
143 | 165 | } |
144 | | - /* |
| 166 | + |
145 | 167 | { |
146 | 168 | //valueless by exception test operator== |
147 | | - ThrowingRange<int*> throwing{3, 5}; |
148 | | - auto concatView_2 = std::views::concat(throwing); |
149 | | - decltype(concatView_2.begin()) it1{}; |
150 | | - decltype(concatView_2.begin()) it2{}; |
| 169 | + flag = false; |
| 170 | + Range<0> r1; |
| 171 | + Range<1> r2; |
| 172 | + |
| 173 | + auto cv = std::views::concat(r1, r2); |
| 174 | + auto iter1 = cv.begin(); |
| 175 | + auto iter2 = std::ranges::next(cv.begin(), 4); |
| 176 | + auto iter3 = cv.begin(); |
| 177 | + flag = true; |
151 | 178 | try { |
152 | | - it1 = concatView_2.begin(); |
| 179 | + iter1 = std::move(iter2); |
153 | 180 | } catch (...) { |
154 | | - std::cout << "Catch is hit" << std::endl; |
155 | | - TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(it1 == it2); }(), "valueless by exception"); |
| 181 | + TEST_LIBCPP_ASSERT_FAILURE( |
| 182 | + [=] { |
| 183 | + (void)(iter1 == iter3); |
| 184 | + }(), |
| 185 | + "valueless by exception"); |
156 | 186 | } |
157 | 187 | } |
158 | 188 |
|
159 | 189 | { |
160 | 190 | //valueless by exception test operator-- |
161 | | - std::ranges::concat_view<ThrowOnCopyView> concatView_2; |
162 | | - std::ranges::iterator_t<std::ranges::concat_view<ThrowOnCopyView>> it1; |
163 | | - try { |
164 | | - it1 = concatView_2.begin(); |
165 | | - } catch (...) { |
166 | | - std::cout << "Catch is hit" << std::endl; |
167 | | - TEST_LIBCPP_ASSERT_FAILURE([&] { (void)--*it1; }(), "valueless by exception"); |
168 | | - } |
169 | | - } |
170 | | -
|
171 | | - { |
172 | | - //valueless by exception test operator* |
173 | | - std::ranges::concat_view<ThrowOnCopyView> concatView_2; |
174 | | - std::ranges::iterator_t<std::ranges::concat_view<ThrowOnCopyView>> it1; |
| 191 | + flag = false; |
| 192 | + Range<0> r1; |
| 193 | + Range<1> r2; |
| 194 | + |
| 195 | + auto cv = std::views::concat(r1, r2); |
| 196 | + auto iter1 = cv.begin(); |
| 197 | + auto iter2 = std::ranges::next(cv.begin(), 4); |
| 198 | + flag = true; |
175 | 199 | try { |
176 | | - it1 = concatView_2.begin(); |
| 200 | + iter1 = std::move(iter2); |
177 | 201 | } catch (...) { |
178 | | - std::cout << "Catch is hit" << std::endl; |
179 | | - TEST_LIBCPP_ASSERT_FAILURE([&] { (void)*it1; }(), "valueless by exception"); |
| 202 | + //ASSERT_SAME_TYPE(decltype(iter1), int); |
| 203 | + TEST_LIBCPP_ASSERT_FAILURE( |
| 204 | + [&] { |
| 205 | + iter1--; |
| 206 | + }(), |
| 207 | + "valueless by exception"); |
180 | 208 | } |
181 | 209 | } |
182 | 210 |
|
183 | 211 | { |
184 | 212 | //valueless by exception test operator++ |
185 | | - std::ranges::concat_view<ThrowOnCopyView> concatView_2; |
186 | | - std::ranges::iterator_t<std::ranges::concat_view<ThrowOnCopyView>> it1; |
| 213 | + flag = false; |
| 214 | + Range<0> r1; |
| 215 | + Range<1> r2; |
| 216 | + |
| 217 | + auto cv = std::views::concat(r1, r2); |
| 218 | + auto iter1 = cv.begin(); |
| 219 | + auto iter2 = std::ranges::next(cv.begin(), 4); |
| 220 | + flag = true; |
187 | 221 | try { |
188 | | - it1 = concatView_2.begin(); |
| 222 | + iter1 = std::move(iter2); |
189 | 223 | } catch (...) { |
190 | | - std::cout << "Catch is hit" << std::endl; |
191 | | - TEST_LIBCPP_ASSERT_FAILURE([&] { (void)++*it1; }(), "valueless by exception"); |
| 224 | + TEST_LIBCPP_ASSERT_FAILURE( |
| 225 | + [&] { |
| 226 | + ++iter1; |
| 227 | + }(), |
| 228 | + "valueless by exception"); |
192 | 229 | } |
193 | 230 | } |
194 | 231 |
|
195 | 232 | { |
196 | 233 | //valueless by exception test operator+= |
197 | | - std::ranges::concat_view<ThrowOnCopyView> concatView_2; |
198 | | - std::ranges::iterator_t<std::ranges::concat_view<ThrowOnCopyView>> it1; |
| 234 | + flag = false; |
| 235 | + Range<0> r1; |
| 236 | + Range<1> r2; |
| 237 | + |
| 238 | + auto cv = std::views::concat(r1, r2); |
| 239 | + auto iter1 = cv.begin(); |
| 240 | + auto iter2 = std::ranges::next(cv.begin(), 4); |
| 241 | + flag = true; |
199 | 242 | try { |
200 | | - it1 = concatView_2.begin(); |
| 243 | + iter1 = std::move(iter2); |
201 | 244 | } catch (...) { |
202 | | - std::cout << "Catch is hit" << std::endl; |
203 | | - TEST_LIBCPP_ASSERT_FAILURE([&] { (void)(it1 += 1); }(), "valueless by exception"); |
| 245 | + TEST_LIBCPP_ASSERT_FAILURE( |
| 246 | + [&] { |
| 247 | + iter1 += 1; |
| 248 | + }(), |
| 249 | + "valueless by exception"); |
204 | 250 | } |
205 | 251 | } |
206 | | - */ |
207 | 252 | } |
0 commit comments