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 // <algorithm>
12
13 // template<input_iterator I, sentinel_for<I> S, weakly_incrementable O,
14 // copy_constructible F, class Proj = identity>
15 // requires indirectly_writable<O, indirect_result_t<F&, projected<I, Proj>>>
16 // constexpr ranges::unary_transform_result<I, O>
17 // ranges::transform(I first1, S last1, O result, F op, Proj proj = {});
18 // template<input_range R, weakly_incrementable O, copy_constructible F,
19 // class Proj = identity>
20 // requires indirectly_writable<O, indirect_result_t<F&, projected<iterator_t<R>, Proj>>>
21 // constexpr ranges::unary_transform_result<borrowed_iterator_t<R>, O>
22 // ranges::transform(R&& r, O result, F op, Proj proj = {});
23
24 #include <algorithm>
25 #include <array>
26 #include <cassert>
27 #include <functional>
28 #include <ranges>
29
30 #include "test_iterators.h"
31 #include "almost_satisfies_types.h"
32
33 template <class Range>
34 concept HasTransformR = requires(Range r, int* out) { std::ranges::transform(r, out, std::identity{}); };
35
36 static_assert(HasTransformR<std::array<int, 1>>);
37 static_assert(!HasTransformR<int>);
38 static_assert(!HasTransformR<InputRangeNotDerivedFrom>);
39 static_assert(!HasTransformR<InputRangeNotIndirectlyReadable>);
40 static_assert(!HasTransformR<InputRangeNotInputOrOutputIterator>);
41 static_assert(!HasTransformR<InputRangeNotSentinelSemiregular>);
42 static_assert(!HasTransformR<InputRangeNotSentinelEqualityComparableWith>);
43
44 template <class It, class Sent = It>
45 concept HasTransformIt =
46 requires(It it, Sent sent, int* out) { std::ranges::transform(it, sent, out, std::identity{}); };
47
48 static_assert(HasTransformIt<int*>);
49 static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);
50 static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);
51 static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);
52 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
53 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
54
55 template <class It>
56 concept HasTransformOut = requires(int* it, int* sent, It out, std::array<int, 2> range) {
57 std::ranges::transform(it, sent, out, std::identity{});
58 std::ranges::transform(range, out, std::identity{});
59 };
60 static_assert(HasTransformOut<int*>);
61 static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);
62
63 // check indirectly_readable
64 static_assert(HasTransformOut<char*>);
65 static_assert(!HasTransformOut<int**>);
66
67 struct MoveOnlyFunctor {
68 MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;
69 MoveOnlyFunctor(MoveOnlyFunctor&&) = default;
70 int operator()(int);
71 };
72
73 template <class Func>
74 concept HasTransformFuncUnary = requires(int* it, int* sent, int* out, std::array<int, 2> range, Func func) {
75 std::ranges::transform(it, sent, out, func);
76 std::ranges::transform(range, out, func);
77 };
78 static_assert(HasTransformFuncUnary<std::identity>);
79 static_assert(!HasTransformFuncUnary<MoveOnlyFunctor>);
80
81 static_assert(std::is_same_v<std::ranges::unary_transform_result<int, long>, std::ranges::in_out_result<int, long>>);
82
83 // clang-format off
84 template <class In1, class Out, class Sent1>
test_iterators()85 constexpr bool test_iterators() {
86 { // simple
87 {
88 int a[] = {1, 2, 3, 4, 5};
89 int b[5];
90 std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =
91 std::ranges::transform(In1(a), Sent1(In1(a + 5)), Out(b), [](int i) { return i * 2; });
92 assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));
93 assert(base(ret.in) == a + 5);
94 assert(base(ret.out) == b + 5);
95 }
96
97 {
98 int a[] = {1, 2, 3, 4, 5};
99 int b[5];
100 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 5)));
101 std::same_as<std::ranges::in_out_result<In1, Out>> decltype(auto) ret =
102 std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
103 assert((std::to_array(b) == std::array{2, 4, 6, 8, 10}));
104 assert(base(ret.in) == a + 5);
105 assert(base(ret.out) == b + 5);
106 }
107 }
108
109 { // first range empty
110 {
111 std::array<int, 0> a = {};
112 int b[5];
113 auto ret = std::ranges::transform(In1(a.data()), Sent1(In1(a.data())), Out(b), [](int i) { return i * 2; });
114 assert(base(ret.in) == a.data());
115 assert(base(ret.out) == b);
116 }
117
118 {
119 std::array<int, 0> a = {};
120 int b[5];
121 auto range = std::ranges::subrange(In1(a.data()), Sent1(In1(a.data())));
122 auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
123 assert(base(ret.in) == a.data());
124 assert(base(ret.out) == b);
125 }
126 }
127
128 { // one element range
129 {
130 int a[] = {2};
131 int b[5];
132 auto ret = std::ranges::transform(In1(a), Sent1(In1(a + 1)), Out(b), [](int i) { return i * 2; });
133 assert(b[0] == 4);
134 assert(base(ret.in) == a + 1);
135 assert(base(ret.out) == b + 1);
136 }
137
138 {
139 int a[] = {2};
140 int b[5];
141 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 1)));
142 auto ret = std::ranges::transform(range, Out(b), [](int i) { return i * 2; });
143 assert(b[0] == 4);
144 assert(base(ret.in) == a + 1);
145 assert(base(ret.out) == b + 1);
146 }
147 }
148
149 { // check that the transform function and projection call counts are correct
150 {
151 int predCount = 0;
152 int projCount = 0;
153 auto pred = [&](int) { ++predCount; return 1; };
154 auto proj = [&](int) { ++projCount; return 0; };
155 int a[] = {1, 2, 3, 4};
156 std::array<int, 4> c;
157 std::ranges::transform(In1(a), Sent1(In1(a + 4)), Out(c.data()), pred, proj);
158 assert(predCount == 4);
159 assert(projCount == 4);
160 assert((c == std::array{1, 1, 1, 1}));
161 }
162 {
163 int predCount = 0;
164 int projCount = 0;
165 auto pred = [&](int) { ++predCount; return 1; };
166 auto proj = [&](int) { ++projCount; return 0; };
167 int a[] = {1, 2, 3, 4};
168 std::array<int, 4> c;
169 auto range = std::ranges::subrange(In1(a), Sent1(In1(a + 4)));
170 std::ranges::transform(range, Out(c.data()), pred, proj);
171 assert(predCount == 4);
172 assert(projCount == 4);
173 assert((c == std::array{1, 1, 1, 1}));
174 }
175 }
176 return true;
177 }
178 // clang-format on
179
180 template <class Out>
test_iterator_in1()181 constexpr void test_iterator_in1() {
182 test_iterators<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>();
183 test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
184 test_iterators<forward_iterator<int*>, Out, forward_iterator<int*>>();
185 test_iterators<bidirectional_iterator<int*>, Out, bidirectional_iterator<int*>>();
186 test_iterators<random_access_iterator<int*>, Out, random_access_iterator<int*>>();
187 test_iterators<contiguous_iterator<int*>, Out, contiguous_iterator<int*>>();
188 test_iterators<int*, Out, int*>();
189 // static_asserting here to avoid hitting the constant evaluation step limit
190 static_assert(test_iterators<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>());
191 static_assert(test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>());
192 static_assert(test_iterators<forward_iterator<int*>, Out, forward_iterator<int*>>());
193 static_assert(test_iterators<bidirectional_iterator<int*>, Out, bidirectional_iterator<int*>>());
194 static_assert(test_iterators<random_access_iterator<int*>, Out, random_access_iterator<int*>>());
195 static_assert(test_iterators<contiguous_iterator<int*>, Out, contiguous_iterator<int*>>());
196 static_assert(test_iterators<int*, Out, int*>());
197 }
198
test()199 constexpr bool test() {
200 { // check that std::ranges::dangling is returned properly
201 std::array<int, 5> b;
202 std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
203 std::ranges::transform(std::array{1, 2, 3, 5, 4}, b.data(), [](int i) { return i * i; });
204 assert((b == std::array{1, 4, 9, 25, 16}));
205 assert(ret.out == b.data() + b.size());
206 }
207
208 { // check that returning another type from the projection works
209 {
210 struct S { int i; int other; };
211 S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
212 std::array<int, 4> b;
213 std::ranges::transform(a, a + 4, b.begin(), [](S s) { return s.i; });
214 assert((b == std::array{0, 1, 3, 10}));
215 }
216 {
217 struct S { int i; int other; };
218 S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
219 std::array<int, 4> b;
220 std::ranges::transform(a, b.begin(), [](S s) { return s.i; });
221 assert((b == std::array{0, 1, 3, 10}));
222 }
223 }
224
225 { // check that std::invoke is used
226 struct S { int i; };
227 S a[] = { S{1}, S{3}, S{2} };
228 std::array<int, 3> b;
229 auto ret = std::ranges::transform(a, b.data(), [](int i) { return i; }, &S::i);
230 assert((b == std::array{1, 3, 2}));
231 assert(ret.out == b.data() + 3);
232 }
233
234 return true;
235 }
236
main(int,char **)237 int main(int, char**) {
238 test_iterator_in1<cpp17_output_iterator<int*>>();
239 test_iterator_in1<cpp20_output_iterator<int*>>();
240 test_iterator_in1<forward_iterator<int*>>();
241 test_iterator_in1<bidirectional_iterator<int*>>();
242 test_iterator_in1<random_access_iterator<int*>>();
243 test_iterator_in1<contiguous_iterator<int*>>();
244 test_iterator_in1<int*>();
245 test();
246 static_assert(test());
247
248 return 0;
249 }
250