Skip to content

Commit 30e7a1b

Browse files
misccoCaseyCarter
andauthored
Implement common_iterator (#1092)
Co-authored-by: Casey Carter <[email protected]>
1 parent d194462 commit 30e7a1b

File tree

9 files changed

+926
-13
lines changed

9 files changed

+926
-13
lines changed

stl/inc/iterator

Lines changed: 505 additions & 0 deletions
Large diffs are not rendered by default.

stl/inc/xmemory

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,6 @@ void _Deallocate(void* _Ptr, size_t _Bytes) noexcept {
223223

224224
#undef _HAS_ALIGNED_NEW
225225

226-
// FUNCTION TEMPLATE _Construct_in_place
227-
template <class _Ty, class... _Types>
228-
void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) {
229-
::new (const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Obj))))
230-
_Ty(_STD forward<_Types>(_Args)...);
231-
}
232-
233226
// FUNCTION TEMPLATE _Global_new
234227
template <class _Ty, class... _Types>
235228
_Ty* _Global_new(_Types&&... _Args) { // acts as "new" while disallowing user overload selection

stl/inc/xutility

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ struct _Get_rebind_alias<_Ty, _Other, void_t<typename _Ty::template rebind<_Othe
119119
using type = typename _Ty::template rebind<_Other>;
120120
};
121121

122+
// FUNCTION TEMPLATE _Construct_in_place
123+
template <class _Ty, class... _Types>
124+
void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) {
125+
::new (const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Obj))))
126+
_Ty(_STD forward<_Types>(_Args)...);
127+
}
128+
122129
// STRUCT TEMPLATE pointer_traits
123130
template <class _Ty>
124131
struct pointer_traits {
@@ -2329,6 +2336,10 @@ _NODISCARD _Ty _Fake_decay_copy(_Ty) noexcept;
23292336
// (2) is well-formed if and only if E is implicitly convertible to T and T is destructible, and
23302337
// (3) is non-throwing if and only if both conversion from decltype((E)) to T and destruction of T are non-throwing.
23312338

2339+
// CONCEPT _Not_same_as
2340+
template <class _Ty1, class _Ty2>
2341+
concept _Not_same_as = !same_as<remove_cvref_t<_Ty1>, remove_cvref_t<_Ty2>>;
2342+
23322343
namespace ranges {
23332344
// VARIABLE TEMPLATE _Has_complete_elements
23342345
template <class>
@@ -3411,10 +3422,6 @@ namespace ranges {
34113422
using is_transparent = int;
34123423
};
34133424

3414-
// CONCEPT _Not_same_as
3415-
template <class _Ty1, class _Ty2>
3416-
concept _Not_same_as = !same_as<remove_cvref_t<_Ty1>, remove_cvref_t<_Ty2>>;
3417-
34183425
// CONCEPT ranges::common_range
34193426
// clang-format off
34203427
template <class _Rng>

tests/std/include/range_algorithm_support.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,16 @@ namespace test {
110110

111111
using _Prevent_inheriting_unwrap = sentinel;
112112

113-
using unwrap = sentinel<Element, IsWrapped::no>;
113+
using unwrap = sentinel<Element, IsWrapped::no>;
114+
using Constinel = sentinel<const Element, Wrapped>;
115+
116+
constexpr operator Constinel() && noexcept {
117+
return Constinel{exchange(ptr_, nullptr)};
118+
}
119+
120+
constexpr operator Constinel() const& noexcept {
121+
return Constinel{ptr_};
122+
}
114123

115124
// clang-format off
116125
[[nodiscard]] constexpr auto _Unwrapped() const noexcept requires (to_bool(Wrapped)) {
@@ -424,7 +433,8 @@ namespace test {
424433
return std::move(*i.ptr_);
425434
}
426435

427-
constexpr friend void iter_swap(iterator const& x, iterator const& y) requires at_least<input> {
436+
constexpr friend void iter_swap(
437+
iterator const& x, iterator const& y) requires at_least<input> && std::swappable<Element> {
428438
ranges::iter_swap(x.ptr_, y.ptr_);
429439
}
430440

tests/std/test.lst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ tests\P0768R1_spaceship_operator
242242
tests\P0769R2_shift_left_shift_right
243243
tests\P0784R7_library_support_for_more_constexpr_containers
244244
tests\P0811R3_midpoint_lerp
245+
tests\P0896R4_common_iterator
246+
tests\P0896R4_common_iterator_death
245247
tests\P0896R4_counted_iterator
246248
tests\P0896R4_counted_iterator_death
247249
tests\P0896R4_P1614R2_comparisons
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\strict_concepts_matrix.lst
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
#include <cassert>
5+
#include <concepts>
6+
#include <iterator>
7+
#include <type_traits>
8+
#include <utility>
9+
10+
#include <range_algorithm_support.hpp>
11+
using namespace std;
12+
using P = pair<int, int>;
13+
14+
// clang-format off
15+
template <class Iter>
16+
concept CanDifference = requires(Iter it) {
17+
{ it - it };
18+
};
19+
20+
template <class Iter>
21+
concept HasProxy = !is_reference_v<iter_reference_t<Iter>>;
22+
// clang-format on
23+
24+
struct instantiator {
25+
template <input_or_output_iterator Iter>
26+
static constexpr void call() {
27+
if constexpr (copyable<Iter>) {
28+
using ConstIter = typename Iter::Consterator;
29+
using Sen = test::sentinel<iter_value_t<Iter>>;
30+
using OSen = test::sentinel<const iter_value_t<Iter>>;
31+
using Cit = common_iterator<Iter, Sen>;
32+
using OCit = common_iterator<ConstIter, OSen>;
33+
P input[3] = {{0, 1}, {0, 2}, {0, 3}};
34+
35+
// [common.iter.types]
36+
{
37+
using iconcept = typename iterator_traits<Cit>::iterator_concept;
38+
if constexpr (forward_iterator<Iter>) {
39+
STATIC_ASSERT(same_as<iconcept, forward_iterator_tag>);
40+
} else {
41+
STATIC_ASSERT(same_as<typename iterator_traits<Cit>::iterator_concept, input_iterator_tag>);
42+
}
43+
44+
using icat = typename iterator_traits<Cit>::iterator_category;
45+
if constexpr (derived_from<icat, forward_iterator_tag>) {
46+
STATIC_ASSERT(same_as<icat, forward_iterator_tag>);
47+
} else {
48+
STATIC_ASSERT(same_as<icat, input_iterator_tag>);
49+
}
50+
51+
using ipointer = typename iterator_traits<Cit>::pointer;
52+
if constexpr (_Has_op_arrow<Iter>) {
53+
STATIC_ASSERT(same_as<ipointer, decltype(declval<const Iter&>().operator->())>);
54+
} else {
55+
STATIC_ASSERT(same_as<ipointer, void>);
56+
}
57+
}
58+
59+
{ // [common.iter.const]
60+
Cit defaultConstructed{};
61+
Cit iterConstructed{Iter{input}};
62+
Cit sentinelConstructed(Sen{});
63+
Cit copyConstructed{defaultConstructed};
64+
copyConstructed = iterConstructed;
65+
66+
OCit conversionConstructed{defaultConstructed};
67+
conversionConstructed = iterConstructed;
68+
69+
OCit conversionConstructedSentinel{sentinelConstructed};
70+
conversionConstructed = iterConstructed;
71+
}
72+
73+
{ // [common.iter.access]
74+
Cit iter{Iter{input}};
75+
assert(*iter == P(0, 1));
76+
assert(iter->first == 0);
77+
assert(iter->second == 1);
78+
if constexpr (HasProxy<Iter>) {
79+
// We return a proxy class here
80+
static_assert(is_class_v<decltype(iter.operator->())>);
81+
} else {
82+
// Either a pointer or the wrapped iterator
83+
static_assert(!is_class_v<decltype(iter.operator->())>);
84+
}
85+
86+
const Cit constIter{Iter{input}};
87+
assert(*constIter == P(0, 1));
88+
assert(constIter->first == 0);
89+
assert(constIter->second == 1);
90+
if constexpr (HasProxy<Iter>) {
91+
// We return a proxy class here
92+
static_assert(is_class_v<decltype(constIter.operator->())>);
93+
} else {
94+
// Either a pointer or the wrapped iterator
95+
static_assert(!is_class_v<decltype(constIter.operator->())>);
96+
}
97+
}
98+
99+
{ // [common.iter.nav]
100+
Cit iter{Iter{input}};
101+
++iter;
102+
assert(*iter == P(0, 2));
103+
104+
assert(*iter++ == P(0, 2));
105+
assert(*iter == P(0, 3));
106+
}
107+
108+
{ // [common.iter.cmp]
109+
// Compare iterator / iterator
110+
assert(Cit{Iter{input}} == Cit{Iter{input}});
111+
assert(Cit{Iter{input}} != Cit{Iter{input + 1}});
112+
113+
// Compare iterator / sentinel
114+
assert(Cit{Iter{input}} == Cit{Sen{input}});
115+
assert(Cit{Sen{input}} != Cit{Iter{input + 1}});
116+
117+
// Compare sentinel / sentinel
118+
assert(Cit{Sen{input}} == Cit{Sen{input}});
119+
assert(Cit{Sen{input}} == Cit{Sen{input + 1}});
120+
121+
if constexpr (CanDifference<Iter>) {
122+
// Difference iterator / iterator
123+
const same_as<iter_difference_t<Iter>> auto diff_it_it = Cit{Iter{input}} - Cit{Iter{input + 1}};
124+
assert(diff_it_it == -1);
125+
126+
// Difference iterator / sentinel
127+
const same_as<iter_difference_t<Iter>> auto diff_it_sen = Cit{Iter{input}} - Cit{Sen{input + 1}};
128+
const same_as<iter_difference_t<Iter>> auto diff_sen_it = Cit{Sen{input + 1}} - Cit{Iter{input}};
129+
assert(diff_it_sen == -1);
130+
assert(diff_sen_it == 1);
131+
132+
// Difference sentinel / sentinel
133+
const same_as<iter_difference_t<Iter>> auto diff_sen_sen = Cit{Sen{input}} - Cit{Sen{input + 1}};
134+
assert(diff_sen_sen == 0);
135+
136+
// Difference iterator / other iterator
137+
const same_as<iter_difference_t<Iter>> auto diff_it_oit = Cit{Iter{input}} - OCit{Iter{input + 1}};
138+
assert(diff_it_oit == -1);
139+
140+
// Difference iterator / other sentinel
141+
const same_as<iter_difference_t<Iter>> auto diff_it_osen = Cit{Iter{input}} - OCit{OSen{input + 1}};
142+
assert(diff_it_osen == -1);
143+
144+
// Difference other iterator / sentinel
145+
const same_as<iter_difference_t<Iter>> auto diff_sen_oit = Cit{Sen{input + 1}} - OCit{Iter{input}};
146+
assert(diff_sen_oit == 1);
147+
148+
// Difference sentinel / other sentinel
149+
const same_as<iter_difference_t<Iter>> auto diff_sen_osen = Cit{Sen{input}} - OCit{OSen{input + 1}};
150+
assert(diff_sen_osen == 0);
151+
}
152+
}
153+
154+
{ // [common.iter.cust]
155+
if constexpr (input_iterator<Iter>) { // iter_move
156+
Cit iter1{Iter{input}};
157+
158+
const same_as<iter_value_t<Iter>> auto element1 = ranges::iter_move(iter1);
159+
assert(element1 == P(0, 1));
160+
}
161+
162+
if constexpr (indirectly_swappable<Iter>) { // iter_swap
163+
Cit iter1{Iter{input}};
164+
Cit iter2{Iter{input + 1}};
165+
166+
ranges::iter_swap(iter1, iter2);
167+
assert(*iter1 == P(0, 2));
168+
assert(*iter2 == P(0, 1));
169+
}
170+
}
171+
}
172+
}
173+
};
174+
175+
bool test_operator_arrow() {
176+
P input[3] = {{0, 1}, {0, 2}, {0, 3}};
177+
178+
using pointerTest = common_iterator<P*, void*>;
179+
pointerTest pointerIter{input};
180+
181+
assert(*pointerIter == P(0, 1));
182+
assert(pointerIter->first == 0);
183+
assert(pointerIter->second == 1);
184+
static_assert(is_same_v<decltype(pointerIter.operator->()), P* const&>);
185+
186+
using countedTest = common_iterator<counted_iterator<P*>, default_sentinel_t>;
187+
countedTest countedIter{counted_iterator{input, 3}};
188+
189+
assert(*countedIter == P(0, 1));
190+
assert(countedIter->first == 0);
191+
assert(countedIter->second == 1);
192+
static_assert(is_same_v<decltype(countedIter.operator->()), P*>);
193+
194+
return true;
195+
}
196+
197+
int main() {
198+
with_writable_iterators<instantiator, P>::call();
199+
200+
test_operator_arrow();
201+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst

0 commit comments

Comments
 (0)