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 I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2,
14 //          weakly_incrementable O, copy_constructible F, class Proj1 = identity,
15 //          class Proj2 = identity>
16 //   requires indirectly_writable<O, indirect_result_t<F&, projected<I1, Proj1>,
17 //                                          projected<I2, Proj2>>>
18 //   constexpr ranges::binary_transform_result<I1, I2, O>
19 //     ranges::transform(I1 first1, S1 last1, I2 first2, S2 last2, O result,
20 //                       F binary_op, Proj1 proj1 = {}, Proj2 proj2 = {});
21 
22 // The range overloads are tested in ranges.transform.binary.range.pass.cpp.
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 struct BinaryFunc {
34   int operator()(int, int);
35 };
36 
37 template <class It, class Sent = It>
38 concept HasTransformIt =
39     requires(It it, Sent sent, int* out) { std::ranges::transform(it, sent, it, sent, out, BinaryFunc{}); };
40 static_assert(HasTransformIt<int*>);
41 static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);
42 static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);
43 static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);
44 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
45 static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
46 
47 template <class It>
48 concept HasTransformOut = requires(int* it, int* sent, It out, std::array<int, 2> range) {
49   std::ranges::transform(it, sent, it, sent, out, BinaryFunc{});
50 };
51 static_assert(HasTransformOut<int*>);
52 static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);
53 
54 // check indirectly_readable
55 static_assert(HasTransformOut<char*>);
56 static_assert(!HasTransformOut<int**>);
57 
58 struct MoveOnlyFunctor {
59   MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;
60   MoveOnlyFunctor(MoveOnlyFunctor&&)      = default;
61   int operator()(int, int);
62 };
63 
64 template <class Func>
65 concept HasTransformFuncBinary = requires(int* it, int* sent, int* out, std::array<int, 2> range, Func func) {
66   std::ranges::transform(it, sent, it, sent, out, func);
67 };
68 static_assert(HasTransformFuncBinary<BinaryFunc>);
69 static_assert(!HasTransformFuncBinary<MoveOnlyFunctor>);
70 
71 static_assert(std::is_same_v<std::ranges::binary_transform_result<int, long, char>,
72                              std::ranges::in_in_out_result<int, long, char>>);
73 
74 // clang-format off
75 template <class In1, class In2, class Out, class Sent1, class Sent2>
test_iterators()76 constexpr bool test_iterators() {
77   { // simple
78     int a[] = {1, 2, 3, 4, 5};
79     int b[] = {5, 4, 3, 2, 1};
80     int c[5];
81 
82     std::same_as<std::ranges::in_in_out_result<In1, In2, Out>> decltype(auto) ret = std::ranges::transform(
83         In1(a), Sent1(In1(a + 5)), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
84 
85     assert((std::to_array(c) == std::array{6, 6, 6, 6, 6}));
86     assert(base(ret.in1) == a + 5);
87     assert(base(ret.in2) == b + 5);
88     assert(base(ret.out) == c + 5);
89   }
90 
91   { // first range empty
92     std::array<int, 0> a = {};
93     int b[] = {5, 4, 3, 2, 1};
94     int c[5];
95 
96     auto ret = std::ranges::transform(
97         In1(a.data()), Sent1(In1(a.data())), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
98 
99     assert(base(ret.in1) == a.data());
100     assert(base(ret.in2) == b);
101     assert(base(ret.out) == c);
102   }
103 
104   { // second range empty
105     int a[] = {5, 4, 3, 2, 1};
106     std::array<int, 0> b = {};
107     int c[5];
108 
109     auto ret = std::ranges::transform(
110         In1(a), Sent1(In1(a + 5)), In2(b.data()), Sent2(In2(b.data())), Out(c), [](int i, int j) { return i + j; });
111 
112     assert(base(ret.in1) == a);
113     assert(base(ret.in2) == b.data());
114     assert(base(ret.out) == c);
115   }
116 
117   { // both ranges empty
118     std::array<int, 0> a = {};
119     std::array<int, 0> b = {};
120     int c[5];
121 
122     auto ret = std::ranges::transform(
123         In1(a.data()), Sent1(In1(a.data())), In2(b.data()), Sent2(In2(b.data())), Out(c), [](int i, int j) { return i + j; });
124 
125     assert(base(ret.in1) == a.data());
126     assert(base(ret.in2) == b.data());
127     assert(base(ret.out) == c);
128   }
129 
130   { // first range one element
131     int a[] = {2};
132     int b[] = {5, 4, 3, 2, 1};
133     int c[5];
134 
135     auto ret = std::ranges::transform(
136         In1(a), Sent1(In1(a + 1)), In2(b), Sent2(In2(b + 5)), Out(c), [](int i, int j) { return i + j; });
137 
138     assert(c[0] == 7);
139     assert(base(ret.in1) == a + 1);
140     assert(base(ret.in2) == b + 1);
141     assert(base(ret.out) == c + 1);
142   }
143 
144   { // second range contains one element
145     int a[] = {5, 4, 3, 2, 1};
146     int b[] = {4};
147     int c[5];
148 
149     auto ret = std::ranges::transform(
150         In1(a), Sent1(In1(a + 5)), In2(b), Sent2(In2(b + 1)), Out(c), [](int i, int j) { return i + j; });
151 
152     assert(c[0] == 9);
153     assert(base(ret.in1) == a + 1);
154     assert(base(ret.in2) == b + 1);
155     assert(base(ret.out) == c + 1);
156   }
157 
158   { // check that the transform function and projection call counts are correct
159     int predCount = 0;
160     int proj1Count = 0;
161     int proj2Count = 0;
162     auto pred = [&](int, int) { ++predCount; return 1; };
163     auto proj1 = [&](int) { ++proj1Count; return 0; };
164     auto proj2 = [&](int) { ++proj2Count; return 0; };
165     int a[] = {1, 2, 3, 4};
166     int b[] = {1, 2, 3, 4};
167     std::array<int, 4> c;
168     std::ranges::transform(In1(a), Sent1(In1(a + 4)), In2(b), Sent2(In2(b + 4)), Out(c.data()), pred, proj1, proj2);
169     assert(predCount == 4);
170     assert(proj1Count == 4);
171     assert(proj2Count == 4);
172     assert((c == std::array{1, 1, 1, 1}));
173   }
174 
175   return true;
176 }
177 // clang-format on
178 
179 template <class In2, class Out, class Sent2 = In2>
test_iterator_in1()180 constexpr void test_iterator_in1() {
181   test_iterators<cpp17_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp17_input_iterator<int*>>, Sent2>();
182   test_iterators<cpp20_input_iterator<int*>, In2, Out, sentinel_wrapper<cpp20_input_iterator<int*>>, Sent2>();
183   test_iterators<forward_iterator<int*>, In2, Out, forward_iterator<int*>, Sent2>();
184   test_iterators<bidirectional_iterator<int*>, In2, Out, bidirectional_iterator<int*>, Sent2>();
185   test_iterators<random_access_iterator<int*>, In2, Out, random_access_iterator<int*>, Sent2>();
186   test_iterators<contiguous_iterator<int*>, In2, Out, contiguous_iterator<int*>, Sent2>();
187   test_iterators<int*, In2, Out, int*, Sent2>();
188 }
189 
190 template <class Out>
test_iterators_in1_in2()191 constexpr void test_iterators_in1_in2() {
192   test_iterator_in1<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>();
193   test_iterator_in1<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
194   test_iterator_in1<forward_iterator<int*>, Out>();
195   test_iterator_in1<bidirectional_iterator<int*>, Out>();
196   test_iterator_in1<random_access_iterator<int*>, Out>();
197   test_iterator_in1<contiguous_iterator<int*>, Out>();
198   test_iterator_in1<int*, Out>();
199 }
200 
test()201 constexpr bool test() {
202   test_iterators_in1_in2<cpp17_output_iterator<int*>>();
203   test_iterators_in1_in2<cpp20_output_iterator<int*>>();
204   test_iterators_in1_in2<forward_iterator<int*>>();
205   test_iterators_in1_in2<bidirectional_iterator<int*>>();
206   test_iterators_in1_in2<random_access_iterator<int*>>();
207   test_iterators_in1_in2<contiguous_iterator<int*>>();
208   test_iterators_in1_in2<int*>();
209 
210   { // check that returning another type from the projection works
211     struct S { int i; int other; };
212     S a[] = { S{0, 0}, S{1, 0}, S{3, 0}, S{10, 0} };
213     S b[] = { S{0, 10}, S{1, 20}, S{3, 30}, S{10, 40} };
214     std::array<int, 4> c;
215     std::ranges::transform(a, a + 4, b, b + 4, c.begin(), [](S s1, S s2) { return s1.i + s2.other; });
216     assert((c == std::array{10, 21, 33, 50}));
217   }
218 
219   return true;
220 }
221 
main(int,char **)222 int main(int, char**) {
223   test();
224   static_assert(test());
225 
226   return 0;
227 }
228