2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <type_traits>
26 #include <folly/Iterator.h>
27 #include <folly/portability/GTest.h>
31 * Container type used for unit tests.
34 using Container = std::deque<T>;
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;
47 * Class that increases various counters to keep track of how objects have
48 * been constructed or assigned to, to verify iterator behavior.
54 Object(const Object&) {
57 Object(Object&&) noexcept {
60 explicit Object(int) {
63 explicit Object(int, int) {
66 Object& operator=(const Object&) {
70 Object& operator=(Object&&) noexcept {
74 Object& operator=(int) noexcept {
81 * Reset all call counters to 0.
83 void init_counters() {
84 gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =
85 gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;
89 * Test for iterator copy and move.
91 template <typename Iterator>
92 void copy_and_move_test(Container<int>& q, Iterator it) {
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});
105 * Test for emplacement with perfect forwarding.
107 template <typename Iterator>
108 void emplace_test(Container<Object>& q, Iterator it) {
109 using folly::make_emplace_args;
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);
145 using namespace folly;
148 * Basic tests for folly::emplace_iterator.
150 TEST(EmplaceIterator, EmplacerTest) {
153 copy_and_move_test(q, emplacer(q, q.begin()));
157 emplace_test(q, emplacer(q, q.begin()));
161 auto it = emplacer(q, q.begin());
165 it = emplacer(q, q.begin());
168 EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));
173 * Basic tests for folly::front_emplace_iterator.
175 TEST(EmplaceIterator, FrontEmplacerTest) {
178 copy_and_move_test(q, front_emplacer(q));
182 emplace_test(q, front_emplacer(q));
186 auto it = front_emplacer(q);
190 it = front_emplacer(q);
193 EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));
198 * Basic tests for folly::back_emplace_iterator.
200 TEST(EmplaceIterator, BackEmplacerTest) {
203 copy_and_move_test(q, back_emplacer(q));
207 emplace_test(q, back_emplacer(q));
211 auto it = back_emplacer(q);
215 it = back_emplacer(q);
218 EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));
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.
227 TEST(EmplaceIterator, Copy) {
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);
244 * Test std::transform() with multi-argument constructors. This would require
245 * a temporary Object with std::back_insert_iterator.
247 TEST(EmplaceIterator, Transform) {
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);
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);
266 * Test multi-argument store and forward.
268 TEST(EmplaceIterator, EmplaceArgs) {
272 const Object& o4 = o3;
276 // Test copy construction.
277 auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));
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);
289 // Test copy assignment.
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);
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);
316 // Test move construction.
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);
328 // Test move assignment.
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);
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.
346 std::tuple_element_t<0, decltype(args)::storage_type>>::value,
351 std::tuple_element_t<1, decltype(args)::storage_type>>::value,
356 std::tuple_element_t<2, decltype(args)::storage_type>>::value,
361 std::tuple_element_t<3, decltype(args)::storage_type>>::value,
366 std::tuple_element_t<4, decltype(args)::storage_type>>::value,
371 std::tuple_element_t<5, decltype(args)::storage_type>>::value,
375 std::reference_wrapper<const Object>,
376 std::tuple_element_t<6, decltype(args)::storage_type>>::value,
380 rvalue_reference_wrapper<Object>,
381 std::tuple_element_t<7, decltype(args)::storage_type>>::value,
384 // Check whether args.get() restores the original argument type for
385 // rvalue references to emplace_args.
387 std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::
391 std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::
397 decltype(get_emplace_arg<2>(std::move(args)))>::value,
400 std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::
406 decltype(get_emplace_arg<4>(std::move(args)))>::value,
409 std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::
415 decltype(get_emplace_arg<6>(std::move(args)))>::value,
418 std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::
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
427 std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value, "");
429 std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value, "");
431 std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value, "");
433 std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value, "");
435 std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value, "");
437 std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value, "");
439 std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value,
445 * Test implicit unpacking.
447 TEST(EmplaceIterator, ImplicitUnpack) {
448 static std::size_t multiCtrCnt;
449 static std::size_t pairCtrCnt;
450 static std::size_t tupleCtrCnt;
456 explicit Object2(const std::pair<int, int>&) {
459 explicit Object2(const std::tuple<int, int>&) {
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);
469 EXPECT_EQ(multiCtrCnt, 2);
470 EXPECT_EQ(pairCtrCnt, 0);
471 EXPECT_EQ(tupleCtrCnt, 0);
473 EXPECT_EQ(multiCtrCnt, 0);
474 EXPECT_EQ(pairCtrCnt, 1);
475 EXPECT_EQ(tupleCtrCnt, 1);
479 Container<Object2> q;
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);