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// Testing std::ranges::iota
10
11// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
12
13#include <algorithm>
14#include <array>
15#include <cassert>
16#include <numeric>
17#include <utility>
18
19#include "almost_satisfies_types.h"
20#include "test_iterators.h"
21#include "test_macros.h"
22
23//
24// Testing constraints
25//
26
27// Concepts to check different overloads of std::ranges::iota
28template <class Iter = int*, class Sent = int*, class Value = int>
29concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
30 std::ranges::iota(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Value>(val));
31};
32
33template <class Range, class Value = int>
34concept HasIotaRange =
35 requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };
36
37// Test constraints of the iterator/sentinel overload
38// ==================================================
39static_assert(HasIotaIter<int*, int*, int>);
40
41// !input_or_output_iterator<O>
42static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);
43
44// !sentinel_for<S, O>
45static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);
46static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
47
48// !weakly_incrementable<T>
49static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);
50
51// !indirectly writable <O, T>
52static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);
53
54// Test constraints for the range overload
55// =======================================
56static_assert(HasIotaRange<UncheckedRange<int*>, int>);
57
58// !weakly_incrementable<T>
59static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
60
61// !ranges::output_range<const _Tp&>
62static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
63
64//
65// Testing results
66//
67
68struct DangerousCopyAssign {
69 int val;
70 using difference_type = int;
71
72 constexpr explicit DangerousCopyAssign(int v) : val(v) {}
73
74 // Needed in postfix
75 constexpr DangerousCopyAssign(DangerousCopyAssign const& other) { this->val = other.val; }
76
77 /*
78 This class has a "mischievous" non-const overload of copy-assignment
79 operator that modifies the object being assigned from. `ranges::iota`
80 should not be invoking this overload thanks to the `std::as_const` in its
81 implementation. If for some reason it does invoke it, there will be a compiler
82 error.
83 */
84 constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) = delete;
85
86 // safe copy assignment std::as_const inside ranges::iota should ensure this
87 // overload gets called
88 constexpr DangerousCopyAssign& operator=(DangerousCopyAssign const& a) {
89 this->val = a.val;
90 return *this;
91 }
92
93 constexpr bool operator==(DangerousCopyAssign const& rhs) { return this->val == rhs.val; }
94
95 // prefix
96 constexpr DangerousCopyAssign& operator++() {
97 ++(this->val);
98 return *this;
99 }
100
101 // postfix
102 constexpr DangerousCopyAssign operator++(int) {
103 auto tmp = *this;
104 ++this->val;
105 return tmp;
106 }
107};
108
109template <class Iter, class Sent, std::size_t N>
110constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {
111 { // (iterator, sentinel) overload
112 auto in_begin = Iter(input.data());
113 auto in_end = Sent(Iter(input.data() + input.size()));
114 std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
115 std::ranges::iota(std::move(in_begin), std::move(in_end), starting_value);
116 assert(result.out == in_end);
117 assert(result.value == starting_value + static_cast<int>(N));
118 assert(std::ranges::equal(input, expected));
119 }
120
121 { // (range) overload
122 // in the range overload adds the additional constraint that it must be an output range
123 // so skip this for the input iterators we test
124 auto in_begin = Iter(input.data());
125 auto in_end = Sent(Iter(input.data() + input.size()));
126 auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end));
127
128 std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
129 std::ranges::iota(range, starting_value);
130 assert(result.out == in_end);
131 assert(result.value == starting_value + static_cast<int>(N));
132 assert(std::ranges::equal(input, expected));
133 }
134}
135
136template <class Iter, class Sent = sentinel_wrapper<Iter>>
137constexpr void test_results() {
138 // Empty
139 test_result<Iter, Sent, 0>({}, 0, {});
140 // 1-element sequence
141 test_result<Iter, Sent, 1>({1}, 0, {0});
142 // Longer sequence
143 test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
144}
145
146constexpr void test_user_defined_type() {
147 // Simple non-fundamental type
148 struct UserDefinedType {
149 int val;
150 using difference_type = int;
151
152 constexpr explicit UserDefinedType(int v) : val(v) {}
153 constexpr UserDefinedType(UserDefinedType const& other) { this->val = other.val; }
154 constexpr UserDefinedType& operator=(UserDefinedType const& a) {
155 this->val = a.val;
156 return *this;
157 }
158
159 // prefix
160 constexpr UserDefinedType& operator++() {
161 ++(this->val);
162 return *this;
163 }
164
165 // postfix
166 constexpr UserDefinedType operator++(int) {
167 auto tmp = *this;
168 ++this->val;
169 return tmp;
170 }
171 };
172
173 // Setup
174 using A = UserDefinedType;
175 std::array<UserDefinedType, 5> a = {A{0}, A{0}, A{0}, A{0}, A{0}};
176 std::array<UserDefinedType, 5> expected = {A{0}, A{1}, A{2}, A{3}, A{4}};
177
178 // Fill with values
179 std::ranges::iota(a, A{0});
180 auto proj_val = [](UserDefinedType const& el) { return el.val; };
181
182 // Check
183 assert(std::ranges::equal(a, expected, std::ranges::equal_to{}, proj_val, proj_val));
184}
185
186constexpr void test_dangerous_copy_assign() {
187 using A = DangerousCopyAssign;
188
189 // If the dangerous non-const copy assignment is called, the final values in
190 // aa should increment by 2 rather than 1.
191 std::array<A, 3> aa = {A{0}, A{0}, A{0}};
192 std::array<A, 3> expected = {A{0}, A{1}, A{2}};
193 std::ranges::iota(aa, A{0});
194 auto proj_val = [](DangerousCopyAssign const& el) { return el.val; };
195 assert(std::ranges::equal(aa, expected, std::ranges::equal_to{}, proj_val, proj_val));
196}
197
198constexpr bool test_results() {
199 // Tests on fundamental types
200 types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
201 test_results<cpp17_output_iterator<int*>>();
202 test_results<cpp20_output_iterator<int*>>();
203 test_results<int*, sized_sentinel<int*>>();
204
205 // Tests on non-fundamental types
206 test_user_defined_type();
207 test_dangerous_copy_assign();
208 return true;
209}
210
211int main(int, char**) {
212 test_results();
213 static_assert(test_results());
214 return 0;
215}
216

source code of libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp