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
33template <class Range>
34concept HasTransformR = requires(Range r, int* out) { std::ranges::transform(r, out, std::identity{}); };
35
36static_assert(HasTransformR<std::array<int, 1>>);
37static_assert(!HasTransformR<int>);
38static_assert(!HasTransformR<InputRangeNotDerivedFrom>);
39static_assert(!HasTransformR<InputRangeNotIndirectlyReadable>);
40static_assert(!HasTransformR<InputRangeNotInputOrOutputIterator>);
41static_assert(!HasTransformR<InputRangeNotSentinelSemiregular>);
42static_assert(!HasTransformR<InputRangeNotSentinelEqualityComparableWith>);
43
44template <class It, class Sent = It>
45concept HasTransformIt =
46 requires(It it, Sent sent, int* out) { std::ranges::transform(it, sent, out, std::identity{}); };
47
48static_assert(HasTransformIt<int*>);
49static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);
50static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);
51static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);
52static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
53static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
54
55template <class It>
56concept 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};
60static_assert(HasTransformOut<int*>);
61static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);
62
63// check indirectly_readable
64static_assert(HasTransformOut<char*>);
65static_assert(!HasTransformOut<int**>);
66
67struct MoveOnlyFunctor {
68 MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;
69 MoveOnlyFunctor(MoveOnlyFunctor&&) = default;
70 int operator()(int);
71};
72
73template <class Func>
74concept 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};
78static_assert(HasTransformFuncUnary<std::identity>);
79static_assert(!HasTransformFuncUnary<MoveOnlyFunctor>);
80
81static_assert(std::is_same_v<std::ranges::unary_transform_result<int, long>, std::ranges::in_out_result<int, long>>);
82
83// clang-format off
84template <class In1, class Out, class Sent1>
85constexpr 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
180template <class Out>
181constexpr 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
199constexpr 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{.i: 0, .other: 0}, S{.i: 1, .other: 0}, S{.i: 3, .other: 0}, S{.i: 10, .other: 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{.i: 0, .other: 0}, S{.i: 1, .other: 0}, S{.i: 3, .other: 0}, S{.i: 10, .other: 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{.i: 1}, S{.i: 3}, S{.i: 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
237int 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

source code of libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/ranges.transform.unary.pass.cpp