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
33struct BinaryFunc {
34 int operator()(int, int);
35};
36
37template <class It, class Sent = It>
38concept HasTransformIt =
39 requires(It it, Sent sent, int* out) { std::ranges::transform(it, sent, it, sent, out, BinaryFunc{}); };
40static_assert(HasTransformIt<int*>);
41static_assert(!HasTransformIt<InputIteratorNotDerivedFrom>);
42static_assert(!HasTransformIt<InputIteratorNotIndirectlyReadable>);
43static_assert(!HasTransformIt<InputIteratorNotInputOrOutputIterator>);
44static_assert(!HasTransformIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
45static_assert(!HasTransformIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
46
47template <class It>
48concept HasTransformOut = requires(int* it, int* sent, It out, std::array<int, 2> range) {
49 std::ranges::transform(it, sent, it, sent, out, BinaryFunc{});
50};
51static_assert(HasTransformOut<int*>);
52static_assert(!HasTransformOut<WeaklyIncrementableNotMovable>);
53
54// check indirectly_readable
55static_assert(HasTransformOut<char*>);
56static_assert(!HasTransformOut<int**>);
57
58struct MoveOnlyFunctor {
59 MoveOnlyFunctor(const MoveOnlyFunctor&) = delete;
60 MoveOnlyFunctor(MoveOnlyFunctor&&) = default;
61 int operator()(int, int);
62};
63
64template <class Func>
65concept 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};
68static_assert(HasTransformFuncBinary<BinaryFunc>);
69static_assert(!HasTransformFuncBinary<MoveOnlyFunctor>);
70
71static_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
75template <class In1, class In2, class Out, class Sent1, class Sent2>
76constexpr 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
179template <class In2, class Out, class Sent2 = In2>
180constexpr 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
190template <class Out>
191constexpr 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
201constexpr 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{.i: 0, .other: 0}, S{.i: 1, .other: 0}, S{.i: 3, .other: 0}, S{.i: 10, .other: 0} };
213 S b[] = { S{.i: 0, .other: 10}, S{.i: 1, .other: 20}, S{.i: 3, .other: 30}, S{.i: 10, .other: 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
222int main(int, char**) {
223 test();
224 static_assert(test());
225
226 return 0;
227}
228

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