proxygen
IteratorTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present 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 <map>
23 #include <set>
24 #include <tuple>
25 #include <type_traits>
26 #include <utility>
27 #include <vector>
28 
31 
32 namespace {
36 template <typename T>
37 using Container = std::deque<T>;
38 
39 // Constructor and assignment operator call counters for struct Object.
40 std::size_t gDefaultCtrCnt;
41 std::size_t gCopyCtrCnt;
42 std::size_t gMoveCtrCnt;
43 std::size_t gExplicitCtrCnt;
44 std::size_t gMultiargCtrCnt;
45 std::size_t gCopyOpCnt;
46 std::size_t gMoveOpCnt;
47 std::size_t gConvertOpCnt;
48 
53 struct Object {
54  Object() {
55  ++gDefaultCtrCnt;
56  }
57  Object(const Object&) {
58  ++gCopyCtrCnt;
59  }
60  Object(Object&&) noexcept {
61  ++gMoveCtrCnt;
62  }
63  explicit Object(int) {
64  ++gExplicitCtrCnt;
65  }
66  explicit Object(int, int) {
67  ++gMultiargCtrCnt;
68  }
69  Object& operator=(const Object&) {
70  ++gCopyOpCnt;
71  return *this;
72  }
73  Object& operator=(Object&&) noexcept {
74  ++gMoveOpCnt;
75  return *this;
76  }
77  Object& operator=(int) noexcept {
78  ++gConvertOpCnt;
79  return *this;
80  }
81 };
82 
86 void init_counters() {
87  gDefaultCtrCnt = gCopyCtrCnt = gMoveCtrCnt = gExplicitCtrCnt =
88  gMultiargCtrCnt = gCopyOpCnt = gMoveOpCnt = gConvertOpCnt = 0;
89 }
90 
94 template <typename Iterator>
95 void copy_and_move_test(Container<int>& q, Iterator it) {
96  assert(q.empty());
97  const auto it2(it); // copy construct
98  it = it2; // copy assign from const
99  it = it; // self assign
100  auto it3(std::move(it)); // move construct
101  it = std::move(it3); // move assign
102  // Make sure iterator still works.
103  it = 4711; // emplace
104  EXPECT_EQ(q, Container<int>{4711});
105 }
106 
110 template <typename Iterator>
111 void emplace_test(Container<Object>& q, Iterator it) {
113  assert(q.empty());
114  init_counters();
115  it = Object{}; // default construct + move construct
116  Object obj; // default construct
117  it = obj; // copy construct
118  it = std::move(obj); // move construct
119  const Object obj2; // default construct
120  it = obj2; // copy construct from const
121  it = std::move(obj2); // copy construct (const defeats move)
122  it = 0; // explicit construct
123  it = make_emplace_args(0, 0); // explicit multiarg construct
124  it = std::make_pair(0, 0); // implicit multiarg construct
125  it = std::make_tuple(0, 0); // implicit multiarg construct
126  auto args = make_emplace_args(Object{}); // default construct + move construct
127  it = args; // copy construct
128  it = const_cast<const decltype(args)&>(args); // copy construct from const
129  it = std::move(args); // move construct
130  auto args2 = std::make_tuple(Object{}); // default construct + move construct
131  it = args2; // (implicit multiarg) copy construct
132  it = std::move(args2); // (implicit multiarg) move construct
133  auto args3 = std::make_pair(0, 0);
134  it = args3; // implicit multiarg construct
135  it = std::move(args3); // implicit multiarg construct
136  ASSERT_EQ(q.size(), 16);
137  EXPECT_EQ(gDefaultCtrCnt, 5);
138  EXPECT_EQ(gCopyCtrCnt, 6);
139  EXPECT_EQ(gMoveCtrCnt, 6);
140  EXPECT_EQ(gExplicitCtrCnt, 1);
141  EXPECT_EQ(gMultiargCtrCnt, 5);
142  EXPECT_EQ(gCopyOpCnt, 0);
143  EXPECT_EQ(gMoveOpCnt, 0);
144  EXPECT_EQ(gConvertOpCnt, 0);
145 }
146 } // namespace
147 
148 using namespace folly;
149 
153 TEST(EmplaceIterator, EmplacerTest) {
154  {
155  Container<int> q;
156  copy_and_move_test(q, emplacer(q, q.begin()));
157  }
158  {
159  Container<Object> q;
160  emplace_test(q, emplacer(q, q.begin()));
161  }
162  {
163  Container<int> q;
164  auto it = emplacer(q, q.begin());
165  it = 0;
166  it = 1;
167  it = 2;
168  it = emplacer(q, q.begin());
169  it = 3;
170  it = 4;
171  EXPECT_EQ(q, Container<int>({3, 4, 0, 1, 2}));
172  }
173 }
174 
178 TEST(EmplaceIterator, FrontEmplacerTest) {
179  {
180  Container<int> q;
182  }
183  {
184  Container<Object> q;
185  emplace_test(q, front_emplacer(q));
186  }
187  {
188  Container<int> q;
189  auto it = front_emplacer(q);
190  it = 0;
191  it = 1;
192  it = 2;
193  it = front_emplacer(q);
194  it = 3;
195  it = 4;
196  EXPECT_EQ(q, Container<int>({4, 3, 2, 1, 0}));
197  }
198 }
199 
203 TEST(EmplaceIterator, BackEmplacerTest) {
204  {
205  Container<int> q;
207  }
208  {
209  Container<Object> q;
210  emplace_test(q, back_emplacer(q));
211  }
212  {
213  Container<int> q;
214  auto it = back_emplacer(q);
215  it = 0;
216  it = 1;
217  it = 2;
218  it = back_emplacer(q);
219  it = 3;
220  it = 4;
221  EXPECT_EQ(q, Container<int>({0, 1, 2, 3, 4}));
222  }
223 }
224 
228 TEST(EmplaceIterator, HintEmplacerTest) {
229  {
230  init_counters();
231  std::map<int, Object> m;
232  auto it = hint_emplacer(m, m.end());
233  it = make_emplace_args(
234  std::piecewise_construct,
235  std::forward_as_tuple(0),
236  std::forward_as_tuple(0));
237  it = make_emplace_args(
238  std::piecewise_construct,
239  std::forward_as_tuple(1),
240  std::forward_as_tuple(0, 0));
241  it = make_emplace_args(
242  std::piecewise_construct,
243  std::forward_as_tuple(2),
244  std::forward_as_tuple(Object{}));
245  ASSERT_EQ(m.size(), 3);
246  EXPECT_EQ(gDefaultCtrCnt, 1);
247  EXPECT_EQ(gCopyCtrCnt, 0);
248  EXPECT_EQ(gMoveCtrCnt, 1);
249  EXPECT_EQ(gExplicitCtrCnt, 1);
250  EXPECT_EQ(gMultiargCtrCnt, 1);
251  EXPECT_EQ(gCopyOpCnt, 0);
252  EXPECT_EQ(gMoveOpCnt, 0);
253  EXPECT_EQ(gConvertOpCnt, 0);
254  }
255  {
256  struct O {
257  explicit O(int i_) : i(i_) {}
258  bool operator<(const O& other) const {
259  return i < other.i;
260  }
261  bool operator==(const O& other) const {
262  return i == other.i;
263  }
264  int i;
265  };
266  std::vector<int> v1 = {0, 1, 2, 3, 4};
267  std::vector<int> v2 = {0, 2, 4};
268  std::set<O> diff;
269  std::set_difference(
270  v1.begin(),
271  v1.end(),
272  v2.begin(),
273  v2.end(),
274  hint_emplacer(diff, diff.end()));
275  std::set<O> expected = {O(1), O(3)};
276  ASSERT_EQ(diff, expected);
277  }
278 }
279 
285 TEST(EmplaceIterator, Copy) {
286  init_counters();
287  Container<int> in({0, 1, 2});
288  Container<Object> out;
289  std::copy(in.begin(), in.end(), back_emplacer(out));
290  EXPECT_EQ(3, out.size());
291  EXPECT_EQ(gDefaultCtrCnt, 0);
292  EXPECT_EQ(gCopyCtrCnt, 0);
293  EXPECT_EQ(gMoveCtrCnt, 0);
294  EXPECT_EQ(gExplicitCtrCnt, 3);
295  EXPECT_EQ(gMultiargCtrCnt, 0);
296  EXPECT_EQ(gCopyOpCnt, 0);
297  EXPECT_EQ(gMoveOpCnt, 0);
298  EXPECT_EQ(gConvertOpCnt, 0);
299 }
300 
305 TEST(EmplaceIterator, Transform) {
306  init_counters();
307  Container<int> in({0, 1, 2});
308  Container<Object> out;
309  std::transform(in.begin(), in.end(), back_emplacer(out), [](int i) {
310  return make_emplace_args(i, i);
311  });
312  EXPECT_EQ(3, out.size());
313  EXPECT_EQ(gDefaultCtrCnt, 0);
314  EXPECT_EQ(gCopyCtrCnt, 0);
315  EXPECT_EQ(gMoveCtrCnt, 0);
316  EXPECT_EQ(gExplicitCtrCnt, 0);
317  EXPECT_EQ(gMultiargCtrCnt, 3);
318  EXPECT_EQ(gCopyOpCnt, 0);
319  EXPECT_EQ(gMoveOpCnt, 0);
320  EXPECT_EQ(gConvertOpCnt, 0);
321 }
322 
326 TEST(EmplaceIterator, EmplaceArgs) {
327  Object o1;
328  const Object o2;
329  Object& o3 = o1;
330  const Object& o4 = o3;
331  Object o5;
332 
333  {
334  // Test copy construction.
335  auto args = make_emplace_args(0, o1, o2, o3, o4, Object{}, std::cref(o2));
336  init_counters();
337  auto args2 = args;
338  EXPECT_EQ(gDefaultCtrCnt, 0);
339  EXPECT_EQ(gCopyCtrCnt, 5);
340  EXPECT_EQ(gMoveCtrCnt, 0);
341  EXPECT_EQ(gExplicitCtrCnt, 0);
342  EXPECT_EQ(gMultiargCtrCnt, 0);
343  EXPECT_EQ(gCopyOpCnt, 0);
344  EXPECT_EQ(gMoveOpCnt, 0);
345  EXPECT_EQ(gConvertOpCnt, 0);
346 
347  // Test copy assignment.
348  init_counters();
349  args = args2;
350  EXPECT_EQ(gDefaultCtrCnt, 0);
351  EXPECT_EQ(gCopyCtrCnt, 0);
352  EXPECT_EQ(gMoveCtrCnt, 0);
353  EXPECT_EQ(gExplicitCtrCnt, 0);
354  EXPECT_EQ(gMultiargCtrCnt, 0);
355  EXPECT_EQ(gCopyOpCnt, 5);
356  EXPECT_EQ(gMoveOpCnt, 0);
357  EXPECT_EQ(gConvertOpCnt, 0);
358  }
359 
360  {
361  // Test RVO.
362  init_counters();
363  auto args = make_emplace_args(
364  0, o1, o2, o3, o4, Object{}, std::cref(o2), rref(std::move(o5)));
365  EXPECT_EQ(gDefaultCtrCnt, 1);
366  EXPECT_EQ(gCopyCtrCnt, 4);
367  EXPECT_EQ(gMoveCtrCnt, 1);
368  EXPECT_EQ(gExplicitCtrCnt, 0);
369  EXPECT_EQ(gMultiargCtrCnt, 0);
370  EXPECT_EQ(gCopyOpCnt, 0);
371  EXPECT_EQ(gMoveOpCnt, 0);
372  EXPECT_EQ(gConvertOpCnt, 0);
373 
374  // Test move construction.
375  init_counters();
376  auto args2 = std::move(args);
377  EXPECT_EQ(gDefaultCtrCnt, 0);
378  EXPECT_EQ(gCopyCtrCnt, 0);
379  EXPECT_EQ(gMoveCtrCnt, 5);
380  EXPECT_EQ(gExplicitCtrCnt, 0);
381  EXPECT_EQ(gMultiargCtrCnt, 0);
382  EXPECT_EQ(gCopyOpCnt, 0);
383  EXPECT_EQ(gMoveOpCnt, 0);
384  EXPECT_EQ(gConvertOpCnt, 0);
385 
386  // Test move assignment.
387  init_counters();
388  args = std::move(args2);
389  EXPECT_EQ(gDefaultCtrCnt, 0);
390  EXPECT_EQ(gCopyCtrCnt, 0);
391  EXPECT_EQ(gMoveCtrCnt, 0);
392  EXPECT_EQ(gExplicitCtrCnt, 0);
393  EXPECT_EQ(gMultiargCtrCnt, 0);
394  EXPECT_EQ(gCopyOpCnt, 0);
395  EXPECT_EQ(gMoveOpCnt, 5);
396  EXPECT_EQ(gConvertOpCnt, 0);
397 
398  // Make sure arguments are stored correctly. lvalues by reference, rvalues
399  // by (moved) copy. Rvalues cannot be stored by reference because they may
400  // refer to an expired temporary by the time they are accessed.
401  static_assert(
402  std::is_same<
403  int,
404  std::tuple_element_t<0, decltype(args)::storage_type>>::value,
405  "");
406  static_assert(
407  std::is_same<
408  Object,
409  std::tuple_element_t<1, decltype(args)::storage_type>>::value,
410  "");
411  static_assert(
412  std::is_same<
413  Object,
414  std::tuple_element_t<2, decltype(args)::storage_type>>::value,
415  "");
416  static_assert(
417  std::is_same<
418  Object,
419  std::tuple_element_t<3, decltype(args)::storage_type>>::value,
420  "");
421  static_assert(
422  std::is_same<
423  Object,
424  std::tuple_element_t<4, decltype(args)::storage_type>>::value,
425  "");
426  static_assert(
427  std::is_same<
428  Object,
429  std::tuple_element_t<5, decltype(args)::storage_type>>::value,
430  "");
431  static_assert(
432  std::is_same<
433  std::reference_wrapper<const Object>,
434  std::tuple_element_t<6, decltype(args)::storage_type>>::value,
435  "");
436  static_assert(
437  std::is_same<
439  std::tuple_element_t<7, decltype(args)::storage_type>>::value,
440  "");
441 
442  // Check whether args.get() restores the original argument type for
443  // rvalue references to emplace_args.
444  static_assert(
445  std::is_same<int&&, decltype(get_emplace_arg<0>(std::move(args)))>::
446  value,
447  "");
448  static_assert(
449  std::is_same<Object&, decltype(get_emplace_arg<1>(std::move(args)))>::
450  value,
451  "");
452  static_assert(
453  std::is_same<
454  const Object&,
455  decltype(get_emplace_arg<2>(std::move(args)))>::value,
456  "");
457  static_assert(
458  std::is_same<Object&, decltype(get_emplace_arg<3>(std::move(args)))>::
459  value,
460  "");
461  static_assert(
462  std::is_same<
463  const Object&,
464  decltype(get_emplace_arg<4>(std::move(args)))>::value,
465  "");
466  static_assert(
467  std::is_same<Object&&, decltype(get_emplace_arg<5>(std::move(args)))>::
468  value,
469  "");
470  static_assert(
471  std::is_same<
472  const Object&,
473  decltype(get_emplace_arg<6>(std::move(args)))>::value,
474  "");
475  static_assert(
476  std::is_same<Object&&, decltype(get_emplace_arg<7>(std::move(args)))>::
477  value,
478  "");
479 
480  // lvalue references to emplace_args should behave mostly like std::tuples.
481  // Note that get_emplace_arg<7>(args) does not compile, because
482  // folly::rvalue_reference_wrappers can only be unwrapped through an rvalue
483  // reference.
484  static_assert(
485  std::is_same<int&, decltype(get_emplace_arg<0>(args))>::value, "");
486  static_assert(
487  std::is_same<Object&, decltype(get_emplace_arg<1>(args))>::value, "");
488  static_assert(
489  std::is_same<Object&, decltype(get_emplace_arg<2>(args))>::value, "");
490  static_assert(
491  std::is_same<Object&, decltype(get_emplace_arg<3>(args))>::value, "");
492  static_assert(
493  std::is_same<Object&, decltype(get_emplace_arg<4>(args))>::value, "");
494  static_assert(
495  std::is_same<Object&, decltype(get_emplace_arg<5>(args))>::value, "");
496  static_assert(
497  std::is_same<const Object&, decltype(get_emplace_arg<6>(args))>::value,
498  "");
499  }
500 }
501 
505 TEST(EmplaceIterator, ImplicitUnpack) {
506  static std::size_t multiCtrCnt;
507  static std::size_t pairCtrCnt;
508  static std::size_t tupleCtrCnt;
509 
510  struct Object2 {
511  Object2(int, int) {
512  ++multiCtrCnt;
513  }
514  explicit Object2(const std::pair<int, int>&) {
515  ++pairCtrCnt;
516  }
517  explicit Object2(const std::tuple<int, int>&) {
518  ++tupleCtrCnt;
519  }
520  };
521 
522  auto test = [](auto&& it, bool expectUnpack) {
523  multiCtrCnt = pairCtrCnt = tupleCtrCnt = 0;
524  it = std::make_pair(0, 0);
525  it = std::make_tuple(0, 0);
526  if (expectUnpack) {
527  EXPECT_EQ(multiCtrCnt, 2);
528  EXPECT_EQ(pairCtrCnt, 0);
529  EXPECT_EQ(tupleCtrCnt, 0);
530  } else {
531  EXPECT_EQ(multiCtrCnt, 0);
532  EXPECT_EQ(pairCtrCnt, 1);
533  EXPECT_EQ(tupleCtrCnt, 1);
534  }
535  };
536 
537  Container<Object2> q;
538 
539  test(emplacer(q, q.begin()), true);
540  test(emplacer<false>(q, q.begin()), false);
541  test(front_emplacer(q), true);
542  test(front_emplacer<false>(q), false);
543  test(back_emplacer(q), true);
544  test(back_emplacer<false>(q), false);
545 }
bool operator==(const char *c, CStringRange::Sentinel)
emplace_iterator< Container, implicit_unpack > emplacer(Container &c, typename Container::iterator i)
Definition: Iterator.h:445
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
front_emplace_iterator< Container, implicit_unpack > front_emplacer(Container &c)
Definition: Iterator.h:477
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static uint64_t test(std::string name, bool fc_, bool dedicated_, bool tc_, bool syncops_, uint64_t base)
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
back_emplace_iterator< Container, implicit_unpack > back_emplacer(Container &c)
Definition: Iterator.h:492
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
PUSHMI_INLINE_VAR constexpr detail::transform_fn transform
Definition: transform.h:158
tuple make_tuple()
Definition: gtest-tuple.h:675
hint_emplace_iterator< Container, implicit_unpack > hint_emplacer(Container &c, typename Container::iterator i)
Definition: Iterator.h:461
constexpr std::decay< T >::type copy(T &&value) noexcept(noexcept(typename std::decay< T >::type(std::forward< T >(value))))
Definition: Utility.h:72
rvalue_reference_wrapper< T > rref(T &&value) noexcept
static map< string, int > m
static const char *const value
Definition: Conv.cpp:50
TEST(EmplaceIterator, EmplacerTest)
emplace_args< Args... > make_emplace_args(Args &&...args) noexcept(noexcept(emplace_args< Args... >(std::forward< Args >(args)...)))
Definition: Iterator.h:92
uint64_t diff(uint64_t a, uint64_t b)
Definition: FutexTest.cpp:135
void copy_and_move_test()
Definition: HazptrTest.cpp:317
std::enable_if< IsLessThanComparable< Value >::value, bool >::type operator<(const Expected< Value, Error > &lhs, const Expected< Value, Error > &rhs)
Definition: Expected.h:1321