1//===------ MappedIteratorTest.cpp - Unit tests for mapped_iterator -------===//
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#include "llvm/ADT/STLExtras.h"
10#include "gtest/gtest.h"
11
12using namespace llvm;
13
14namespace {
15
16template <typename T> class MappedIteratorTestBasic : public testing::Test {};
17
18struct Plus1Lambda {
19 auto operator()() const {
20 return [](int X) { return X + 1; };
21 }
22};
23
24struct Plus1LambdaWithCapture {
25 const int One = 1;
26
27 auto operator()() const {
28 return [=](int X) { return X + One; };
29 }
30};
31
32struct Plus1FunctionRef {
33 static int plus1(int X) { return X + 1; }
34
35 using FuncT = int (&)(int);
36
37 FuncT operator()() const { return (FuncT)*plus1; }
38};
39
40struct Plus1FunctionPtr {
41 static int plus1(int X) { return X + 1; }
42
43 using FuncT = int (*)(int);
44
45 FuncT operator()() const { return plus1; }
46};
47
48struct Plus1Functor {
49 struct Plus1 {
50 int operator()(int X) const { return X + 1; }
51 };
52
53 auto operator()() const { return Plus1(); }
54};
55
56struct Plus1FunctorNotDefaultConstructible {
57 class PlusN {
58 const int N;
59
60 public:
61 PlusN(int NArg) : N(NArg) {}
62
63 int operator()(int X) const { return X + N; }
64 };
65
66 auto operator()() const { return PlusN(1); }
67};
68
69// clang-format off
70using FunctionTypes =
71 ::testing::Types<
72 Plus1Lambda,
73 Plus1LambdaWithCapture,
74 Plus1FunctionRef,
75 Plus1FunctionPtr,
76 Plus1Functor,
77 Plus1FunctorNotDefaultConstructible
78 >;
79// clang-format on
80
81TYPED_TEST_SUITE(MappedIteratorTestBasic, FunctionTypes, );
82
83template <typename T> using GetFuncT = decltype(std::declval<T>().operator()());
84
85TYPED_TEST(MappedIteratorTestBasic, DefaultConstruct) {
86 using FuncT = GetFuncT<TypeParam>;
87 using IterT = mapped_iterator<typename std::vector<int>::iterator, FuncT>;
88 TypeParam GetCallable;
89
90 auto Func = GetCallable();
91 (void)Func;
92 constexpr bool DefaultConstruct =
93 std::is_default_constructible_v<callable_detail::Callable<FuncT>>;
94 EXPECT_TRUE(DefaultConstruct);
95 EXPECT_TRUE(std::is_default_constructible_v<IterT>);
96
97 if constexpr (std::is_default_constructible_v<IterT>) {
98 IterT I;
99 (void)I;
100 }
101}
102
103TYPED_TEST(MappedIteratorTestBasic, CopyConstruct) {
104 std::vector<int> V({0});
105
106 using FuncT = GetFuncT<TypeParam>;
107 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
108
109 EXPECT_TRUE(std::is_copy_constructible_v<IterT>);
110
111 if constexpr (std::is_copy_constructible_v<IterT>) {
112 TypeParam GetCallable;
113
114 IterT I1(V.begin(), GetCallable());
115 IterT I2(I1);
116
117 EXPECT_EQ(I2, I1) << "copy constructed iterator is a different position";
118 }
119}
120
121TYPED_TEST(MappedIteratorTestBasic, MoveConstruct) {
122 std::vector<int> V({0});
123
124 using FuncT = GetFuncT<TypeParam>;
125 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
126
127 EXPECT_TRUE(std::is_move_constructible_v<IterT>);
128
129 if constexpr (std::is_move_constructible_v<IterT>) {
130 TypeParam GetCallable;
131
132 IterT I1(V.begin(), GetCallable());
133 IterT I2(V.begin(), GetCallable());
134 IterT I3(std::move(I2));
135
136 EXPECT_EQ(I3, I1) << "move constructed iterator is a different position";
137 }
138}
139
140TYPED_TEST(MappedIteratorTestBasic, CopyAssign) {
141 std::vector<int> V({0});
142
143 using FuncT = GetFuncT<TypeParam>;
144 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
145
146 EXPECT_TRUE(std::is_copy_assignable_v<IterT>);
147
148 if constexpr (std::is_copy_assignable_v<IterT>) {
149 TypeParam GetCallable;
150
151 IterT I1(V.begin(), GetCallable());
152 IterT I2(V.end(), GetCallable());
153
154 I2 = I1;
155
156 EXPECT_EQ(I2, I1) << "copy assigned iterator is a different position";
157 }
158}
159
160TYPED_TEST(MappedIteratorTestBasic, MoveAssign) {
161 std::vector<int> V({0});
162
163 using FuncT = GetFuncT<TypeParam>;
164 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
165
166 EXPECT_TRUE(std::is_move_assignable_v<IterT>);
167
168 if constexpr (std::is_move_assignable_v<IterT>) {
169 TypeParam GetCallable;
170
171 IterT I1(V.begin(), GetCallable());
172 IterT I2(V.begin(), GetCallable());
173 IterT I3(V.end(), GetCallable());
174
175 I3 = std::move(I2);
176
177 EXPECT_EQ(I3, I1) << "move assigned iterator is a different position";
178 }
179}
180
181TYPED_TEST(MappedIteratorTestBasic, GetFunction) {
182 std::vector<int> V({0});
183
184 using FuncT = GetFuncT<TypeParam>;
185 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
186
187 TypeParam GetCallable;
188 IterT I(V.begin(), GetCallable());
189
190 EXPECT_EQ(I.getFunction()(200), 201);
191}
192
193TYPED_TEST(MappedIteratorTestBasic, GetCurrent) {
194 std::vector<int> V({0});
195
196 using FuncT = GetFuncT<TypeParam>;
197 using IterT = mapped_iterator<decltype(V)::iterator, FuncT>;
198
199 TypeParam GetCallable;
200 IterT I(V.begin(), GetCallable());
201
202 EXPECT_EQ(I.getCurrent(), V.begin());
203 EXPECT_EQ(std::next(I).getCurrent(), V.end());
204}
205
206TYPED_TEST(MappedIteratorTestBasic, ApplyFunctionOnDereference) {
207 std::vector<int> V({0});
208 TypeParam GetCallable;
209
210 auto I = map_iterator(V.begin(), GetCallable());
211
212 EXPECT_EQ(*I, 1) << "should have applied function in dereference";
213}
214
215TEST(MappedIteratorTest, ApplyFunctionOnArrow) {
216 struct S {
217 int Z = 0;
218 };
219
220 std::vector<int> V({0});
221 S Y;
222 S *P = &Y;
223
224 auto I = map_iterator(I: V.begin(), F: [&](int X) -> S & { return *(P + X); });
225
226 I->Z = 42;
227
228 EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow";
229}
230
231TEST(MappedIteratorTest, FunctionPreservesReferences) {
232 std::vector<int> V({1});
233 std::map<int, int> M({{1, 1}});
234
235 auto I = map_iterator(I: V.begin(), F: [&](int X) -> int & { return M[X]; });
236 *I = 42;
237
238 EXPECT_EQ(M[1], 42) << "assignment should have modified M";
239}
240
241TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnDereference) {
242 struct CustomMapIterator
243 : public llvm::mapped_iterator_base<CustomMapIterator,
244 std::vector<int>::iterator, int> {
245 using BaseT::BaseT;
246
247 /// Map the element to the iterator result type.
248 int mapElement(int X) const { return X + 1; }
249 };
250
251 std::vector<int> V({0});
252
253 CustomMapIterator I(V.begin());
254
255 EXPECT_EQ(*I, 1) << "should have applied function in dereference";
256}
257
258TEST(MappedIteratorTest, CustomIteratorApplyFunctionOnArrow) {
259 struct S {
260 int Z = 0;
261 };
262 struct CustomMapIterator
263 : public llvm::mapped_iterator_base<CustomMapIterator,
264 std::vector<int>::iterator, S &> {
265 CustomMapIterator(std::vector<int>::iterator it, S *P) : BaseT(it), P(P) {}
266
267 /// Map the element to the iterator result type.
268 S &mapElement(int X) const { return *(P + X); }
269
270 S *P;
271 };
272
273 std::vector<int> V({0});
274 S Y;
275
276 CustomMapIterator I(V.begin(), &Y);
277
278 I->Z = 42;
279
280 EXPECT_EQ(Y.Z, 42) << "should have applied function during arrow";
281}
282
283TEST(MappedIteratorTest, CustomIteratorFunctionPreservesReferences) {
284 struct CustomMapIterator
285 : public llvm::mapped_iterator_base<CustomMapIterator,
286 std::vector<int>::iterator, int &> {
287 CustomMapIterator(std::vector<int>::iterator it, std::map<int, int> &M)
288 : BaseT(it), M(M) {}
289
290 /// Map the element to the iterator result type.
291 int &mapElement(int X) const { return M[X]; }
292
293 std::map<int, int> &M;
294 };
295 std::vector<int> V({1});
296 std::map<int, int> M({{1, 1}});
297
298 auto I = CustomMapIterator(V.begin(), M);
299 *I = 42;
300
301 EXPECT_EQ(M[1], 42) << "assignment should have modified M";
302}
303
304} // anonymous namespace
305

source code of llvm/unittests/ADT/MappedIteratorTest.cpp