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 | |
12 | using namespace llvm; |
13 | |
14 | namespace { |
15 | |
16 | template <typename T> class MappedIteratorTestBasic : public testing::Test {}; |
17 | |
18 | struct Plus1Lambda { |
19 | auto operator()() const { |
20 | return [](int X) { return X + 1; }; |
21 | } |
22 | }; |
23 | |
24 | struct Plus1LambdaWithCapture { |
25 | const int One = 1; |
26 | |
27 | auto operator()() const { |
28 | return [=](int X) { return X + One; }; |
29 | } |
30 | }; |
31 | |
32 | struct 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 | |
40 | struct 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 | |
48 | struct Plus1Functor { |
49 | struct Plus1 { |
50 | int operator()(int X) const { return X + 1; } |
51 | }; |
52 | |
53 | auto operator()() const { return Plus1(); } |
54 | }; |
55 | |
56 | struct 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 |
70 | using FunctionTypes = |
71 | ::testing::Types< |
72 | Plus1Lambda, |
73 | Plus1LambdaWithCapture, |
74 | Plus1FunctionRef, |
75 | Plus1FunctionPtr, |
76 | Plus1Functor, |
77 | Plus1FunctorNotDefaultConstructible |
78 | >; |
79 | // clang-format on |
80 | |
81 | TYPED_TEST_SUITE(MappedIteratorTestBasic, FunctionTypes, ); |
82 | |
83 | template <typename T> using GetFuncT = decltype(std::declval<T>().operator()()); |
84 | |
85 | TYPED_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 | |
103 | TYPED_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 | |
121 | TYPED_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 | |
140 | TYPED_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 | |
160 | TYPED_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 | |
181 | TYPED_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 | |
193 | TYPED_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 | |
206 | TYPED_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 | |
215 | TEST(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 | |
231 | TEST(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 | |
241 | TEST(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 | |
258 | TEST(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 | |
283 | TEST(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 | |