back_emplace_iterator and related classes and utility functions
[folly.git] / folly / test / IteratorTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <algorithm>
18 #include <cassert>
19 #include <cstddef>
20 #include <deque>
21 #include <functional>
22 #include <tuple>
23 #include <type_traits>
24 #include <utility>
25
26 #include <folly/Iterator.h>
27 #include <folly/portability/GTest.h>
28
29 namespace {
30 /**
31  * Container type used for unit tests.
32  */
33 template <typename T>
34 using Container = std::deque<T>;
35
36 // Constructor and assignment operator call counters for struct Object.
37 std::size_t gDefaultCtrCnt;
38 std::size_t gCopyCtrCnt;
39 std::size_t gMoveCtrCnt;
40 std::size_t gExplicitCtrCnt;
41 std::size_t gMultiargCtrCnt;
42 std::size_t gCopyOpCnt;
43 std::size_t gMoveOpCnt;
44 std::size_t gConvertOpCnt;
45
46 /**
47  * Class that increases various counters to keep track of how objects have
48  * been constructed or assigned to, to verify iterator behavior.
49  */
50 struct Object {
51   Object() {
52     ++gDefaultCtrCnt;
53   }
54   Object(const Object&) {
55     ++gCopyCtrCnt;
56   }
57   Object(Object&&) noexcept {
58     ++gMoveCtrCnt;
59   }
60   explicit Object(int) {
61     ++gExplicitCtrCnt;
62   }
63   explicit Object(int, int) {
64     ++gMultiargCtrCnt;
65   }
66   Object& operator=(const Object&) {
67     ++gCopyOpCnt;
68     return *this;
69   }
70   Object& operator=(Object&&) noexcept {
71     ++gMoveOpCnt;
72     return *this;
73   }
74   Object& operator=(int) noexcept {
75     ++gConvertOpCnt;
76     return *this;
77   }
78 };
79
80 /**
81  * Reset all call counters to 0.
82  */
83 void init_counters() {
84   gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =
85       gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;
86 }
87
88 /**
89  * Test for iterator copy and move.
90  */
91 template <typename Iterator>
92 void copy_and_move_test(Container<int>& q, Iterator it) {
93   assert(q.empty());
94   const auto it2(it); // copy construct
95   it = it2; // copy assign from const
96   it = it; // self assign
97   auto it3(std::move(it)); // move construct
98   it = std::move(it3); // move assign
99   // Make sure iterator still works.
100   it = 4711; // emplace
101   EXPECT_EQ(q, Container<int>{4711});
102 }
103
104 /**
105  * Test for emplacement with perfect forwarding.
106  */
107 template <typename Iterator>
108 void emplace_test(Container<Object>& q, Iterator it) {
109   using folly::make_emplace_args;
110   assert(q.empty());
111   init_counters();
112   it = Object{}; // default construct + move construct
113   Object obj; // default construct
114   it = obj; // copy construct
115   it = std::move(obj); // move construct
116   const Object obj2; // default construct
117   it = obj2; // copy construct from const
118   it = std::move(obj2); // copy construct (const defeats move)
119   it = 0; // explicit construct
120   it = make_emplace_args(0, 0); // explicit multiarg construct
121   it = std::make_pair(0, 0); // implicit multiarg construct
122   it = std::make_tuple(0, 0); // implicit multiarg construct
123   auto args = make_emplace_args(Object{}); // default construct + move construct
124   it = args; // copy construct
125   it = const_cast<const decltype(args)&>(args); // copy construct from const
126   it = std::move(args); // move construct
127   auto args2 = std::make_tuple(Object{}); // default construct + move construct
128   it = args2; // (implicit multiarg) copy construct
129   it = std::move(args2); // (implicit multiarg) move construct
130   auto args3 = std::make_pair(0, 0);
131   it = args3; // implicit multiarg construct
132   it = std::move(args3); // implicit multiarg construct
133   ASSERT_EQ(q.size(), 16);
134   EXPECT_EQ(gDefaultCtrCnt, 5);
135   EXPECT_EQ(gCopyCtrCnt, 6);
136   EXPECT_EQ(gMoveCtrCnt, 6);
137   EXPECT_EQ(gExplicitCtrCnt, 1);
138   EXPECT_EQ(gMultiargCtrCnt, 5);
139   EXPECT_EQ(gCopyOpCnt, 0);
140   EXPECT_EQ(gMoveOpCnt, 0);
141   EXPECT_EQ(gConvertOpCnt, 0);
142 }
143 }
144
145 using namespace folly;
146
147 /**
148  * Basic tests for folly::emplace_iterator.
149  */
150 TEST(EmplaceIterator, EmplacerTest) {
151   {
152     Container<int> q;
153     copy_and_move_test(q, emplacer(q, q.begin()));
154   }
155   {
156     Container<Object> q;
157     emplace_test(q, emplacer(q, q.begin()));
158   }
159   {
160     Container<int> q;
161     auto it = emplacer(q, q.begin());
162     it = 0;
163     it = 1;
164     it = 2;
165     it = emplacer(q, q.begin());
166     it = 3;
167     it = 4;
168     EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));
169   }
170 }
171
172 /**
173  * Basic tests for folly::front_emplace_iterator.
174  */
175 TEST(EmplaceIterator, FrontEmplacerTest) {
176   {
177     Container<int> q;
178     copy_and_move_test(q, front_emplacer(q));
179   }
180   {
181     Container<Object> q;
182     emplace_test(q, front_emplacer(q));
183   }
184   {
185     Container<int> q;
186     auto it = front_emplacer(q);
187     it = 0;
188     it = 1;
189     it = 2;
190     it = front_emplacer(q);
191     it = 3;
192     it = 4;
193     EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));
194   }
195 }
196
197 /**
198  * Basic tests for folly::back_emplace_iterator.
199  */
200 TEST(EmplaceIterator, BackEmplacerTest) {
201   {
202     Container<int> q;
203     copy_and_move_test(q, back_emplacer(q));
204   }
205   {
206     Container<Object> q;
207     emplace_test(q, back_emplacer(q));
208   }
209   {
210     Container<int> q;
211     auto it = back_emplacer(q);
212     it = 0;
213     it = 1;
214     it = 2;
215     it = back_emplacer(q);
216     it = 3;
217     it = 4;
218     EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));
219   }
220 }
221
222 /**
223  * Test std::copy() with explicit conversion. This would not compile with a
224  * std::back_insert_iterator, because the constructor of Object that takes a
225  * single int is explicit.
226  */
227 TEST(EmplaceIterator, Copy) {
228   init_counters();
229   Container<int> in({0, 1, 2});
230   Container<Object> out;
231   std::copy(in.begin(), in.end(), back_emplacer(out));
232   EXPECT_EQ(3, out.size());
233   EXPECT_EQ(gDefaultCtrCnt, 0);
234   EXPECT_EQ(gCopyCtrCnt, 0);
235   EXPECT_EQ(gMoveCtrCnt, 0);
236   EXPECT_EQ(gExplicitCtrCnt, 3);
237   EXPECT_EQ(gMultiargCtrCnt, 0);
238   EXPECT_EQ(gCopyOpCnt, 0);
239   EXPECT_EQ(gMoveOpCnt, 0);
240   EXPECT_EQ(gConvertOpCnt, 0);
241 }
242
243 /**
244  * Test std::transform() with multi-argument constructors. This would require
245  * a temporary Object with std::back_insert_iterator.
246  */
247 TEST(EmplaceIterator, Transform) {
248   init_counters();
249   Container<int> in({0, 1, 2});
250   Container<Object> out;
251   std::transform(in.begin(), in.end(), back_emplacer(out), [](int i) {
252     return make_emplace_args(i, i);
253   });
254   EXPECT_EQ(3, out.size());
255   EXPECT_EQ(gDefaultCtrCnt, 0);
256   EXPECT_EQ(gCopyCtrCnt, 0);
257   EXPECT_EQ(gMoveCtrCnt, 0);
258   EXPECT_EQ(gExplicitCtrCnt, 0);
259   EXPECT_EQ(gMultiargCtrCnt, 3);
260   EXPECT_EQ(gCopyOpCnt, 0);
261   EXPECT_EQ(gMoveOpCnt, 0);
262   EXPECT_EQ(gConvertOpCnt, 0);
263 }
264
265 /**
266  * Test multi-argument store and forward.
267  */
268 TEST(EmplaceIterator, EmplaceArgs) {
269   Object o1;
270   const Object o2;
271   Object& o3 = o1;
272   const Object& o4 = o3;
273   Object o5;
274
275   {
276     // Test copy construction.
277     auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));
278     init_counters();
279     auto args2 = args;
280     EXPECT_EQ(gDefaultCtrCnt, 0);
281     EXPECT_EQ(gCopyCtrCnt, 5);
282     EXPECT_EQ(gMoveCtrCnt, 0);
283     EXPECT_EQ(gExplicitCtrCnt, 0);
284     EXPECT_EQ(gMultiargCtrCnt, 0);
285     EXPECT_EQ(gCopyOpCnt, 0);
286     EXPECT_EQ(gMoveOpCnt, 0);
287     EXPECT_EQ(gConvertOpCnt, 0);
288
289     // Test copy assignment.
290     init_counters();
291     args = args2;
292     EXPECT_EQ(gDefaultCtrCnt, 0);
293     EXPECT_EQ(gCopyCtrCnt, 0);
294     EXPECT_EQ(gMoveCtrCnt, 0);
295     EXPECT_EQ(gExplicitCtrCnt, 0);
296     EXPECT_EQ(gMultiargCtrCnt, 0);
297     EXPECT_EQ(gCopyOpCnt, 5);
298     EXPECT_EQ(gMoveOpCnt, 0);
299     EXPECT_EQ(gConvertOpCnt, 0);
300   }
301
302   {
303     // Test RVO.
304     init_counters();
305     auto args = make_emplace_args(
306         0, o1, o2, o3, o4, Object{}, std::cref(o2), rref(std::move(o5)));
307     EXPECT_EQ(gDefaultCtrCnt, 1);
308     EXPECT_EQ(gCopyCtrCnt, 4);
309     EXPECT_EQ(gMoveCtrCnt, 1);
310     EXPECT_EQ(gExplicitCtrCnt, 0);
311     EXPECT_EQ(gMultiargCtrCnt, 0);
312     EXPECT_EQ(gCopyOpCnt, 0);
313     EXPECT_EQ(gMoveOpCnt, 0);
314     EXPECT_EQ(gConvertOpCnt, 0);
315
316     // Test move construction.
317     init_counters();
318     auto args2 = std::move(args);
319     EXPECT_EQ(gDefaultCtrCnt, 0);
320     EXPECT_EQ(gCopyCtrCnt, 0);
321     EXPECT_EQ(gMoveCtrCnt, 5);
322     EXPECT_EQ(gExplicitCtrCnt, 0);
323     EXPECT_EQ(gMultiargCtrCnt, 0);
324     EXPECT_EQ(gCopyOpCnt, 0);
325     EXPECT_EQ(gMoveOpCnt, 0);
326     EXPECT_EQ(gConvertOpCnt, 0);
327
328     // Test move assignment.
329     init_counters();
330     args = std::move(args2);
331     EXPECT_EQ(gDefaultCtrCnt, 0);
332     EXPECT_EQ(gCopyCtrCnt, 0);
333     EXPECT_EQ(gMoveCtrCnt, 0);
334     EXPECT_EQ(gExplicitCtrCnt, 0);
335     EXPECT_EQ(gMultiargCtrCnt, 0);
336     EXPECT_EQ(gCopyOpCnt, 0);
337     EXPECT_EQ(gMoveOpCnt, 5);
338     EXPECT_EQ(gConvertOpCnt, 0);
339
340     // Make sure arguments are stored correctly. lvalues by reference, rvalues
341     // by (moved) copy. Rvalues cannot be stored by reference because they may
342     // refer to an expired temporary by the time they are accessed.
343     static_assert(
344         std::is_same<
345             int,
346             std::tuple_element_t<0, decltype(args)::storage_type>>::value,
347         "");
348     static_assert(
349         std::is_same<
350             Object,
351             std::tuple_element_t<1, decltype(args)::storage_type>>::value,
352         "");
353     static_assert(
354         std::is_same<
355             Object,
356             std::tuple_element_t<2, decltype(args)::storage_type>>::value,
357         "");
358     static_assert(
359         std::is_same<
360             Object,
361             std::tuple_element_t<3, decltype(args)::storage_type>>::value,
362         "");
363     static_assert(
364         std::is_same<
365             Object,
366             std::tuple_element_t<4, decltype(args)::storage_type>>::value,
367         "");
368     static_assert(
369         std::is_same<
370             Object,
371             std::tuple_element_t<5, decltype(args)::storage_type>>::value,
372         "");
373     static_assert(
374         std::is_same<
375             std::reference_wrapper<const Object>,
376             std::tuple_element_t<6, decltype(args)::storage_type>>::value,
377         "");
378     static_assert(
379         std::is_same<
380             rvalue_reference_wrapper<Object>,
381             std::tuple_element_t<7, decltype(args)::storage_type>>::value,
382         "");
383
384     // Check whether args.get() restores the original argument type for
385     // rvalue references to emplace_args.
386     static_assert(
387         std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::
388             value,
389         "");
390     static_assert(
391         std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::
392             value,
393         "");
394     static_assert(
395         std::is_same<
396             const Object&,
397             decltype(get_emplace_arg<2>(std::move(args)))>::value,
398         "");
399     static_assert(
400         std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::
401             value,
402         "");
403     static_assert(
404         std::is_same<
405             const Object&,
406             decltype(get_emplace_arg<4>(std::move(args)))>::value,
407         "");
408     static_assert(
409         std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::
410             value,
411         "");
412     static_assert(
413         std::is_same<
414             const Object&,
415             decltype(get_emplace_arg<6>(std::move(args)))>::value,
416         "");
417     static_assert(
418         std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::
419             value,
420         "");
421
422     // lvalue references to emplace_args should behave mostly like std::tuples.
423     // Note that get_emplace_arg<7>(args) does not compile, because
424     // folly::rvalue_reference_wrappers can only be unwrapped through an rvalue
425     // reference.
426     static_assert(
427         std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value, "");
428     static_assert(
429         std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value, "");
430     static_assert(
431         std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value, "");
432     static_assert(
433         std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value, "");
434     static_assert(
435         std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value, "");
436     static_assert(
437         std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value, "");
438     static_assert(
439         std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value,
440         "");
441   }
442 }
443
444 /**
445  * Test implicit unpacking.
446  */
447 TEST(EmplaceIterator, ImplicitUnpack) {
448   static std::size_t multiCtrCnt;
449   static std::size_t pairCtrCnt;
450   static std::size_t tupleCtrCnt;
451
452   struct Object2 {
453     Object2(int, int) {
454       ++multiCtrCnt;
455     }
456     explicit Object2(const std::pair<int, int>&) {
457       ++pairCtrCnt;
458     }
459     explicit Object2(const std::tuple<int, int>&) {
460       ++tupleCtrCnt;
461     }
462   };
463
464   auto test = [](auto&& it, bool expectUnpack) {
465     multiCtrCnt = pairCtrCnt = tupleCtrCnt = 0;
466     it = std::make_pair(0, 0);
467     it = std::make_tuple(0, 0);
468     if (expectUnpack) {
469       EXPECT_EQ(multiCtrCnt, 2);
470       EXPECT_EQ(pairCtrCnt, 0);
471       EXPECT_EQ(tupleCtrCnt, 0);
472     } else {
473       EXPECT_EQ(multiCtrCnt, 0);
474       EXPECT_EQ(pairCtrCnt, 1);
475       EXPECT_EQ(tupleCtrCnt, 1);
476     }
477   };
478
479   Container<Object2> q;
480
481   test(emplacer(q, q.begin()), true);
482   test(emplacer<false>(q, q.begin()), false);
483   test(front_emplacer(q), true);
484   test(front_emplacer<false>(q), false);
485   test(back_emplacer(q), true);
486   test(back_emplacer<false>(q), false);
487 }