2 // Copyright 2006-2009 Daniel James.
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 #include "../helpers/prefix.hpp"
8 #include <boost/unordered_set.hpp>
9 #include <boost/unordered_map.hpp>
10 #include "../helpers/postfix.hpp"
13 #include "../helpers/test.hpp"
15 namespace unnecessary_copy_tests
{
19 BOOST_COPYABLE_AND_MOVABLE(count_copies
)
25 count_copies() : tag_(0), id_(++id_count
)
28 trace_op("Default construct");
31 explicit count_copies(int tag
) : tag_(tag
), id_(++id_count
)
34 trace_op("Tag construct");
37 // This bizarre constructor is an attempt to confuse emplace.
39 // unordered_map<count_copies, count_copies> x:
40 // x.emplace(count_copies(1), count_copies(2));
41 // x.emplace(count_copies(1), count_copies(2), count_copies(3));
43 // The first emplace should use the single argument constructor twice.
44 // The second emplace should use the single argument contructor for
45 // the key, and this constructor for the value.
46 count_copies(count_copies
const&, count_copies
const& x
)
47 : tag_(x
.tag_
), id_(++id_count
)
50 trace_op("Pair construct");
53 count_copies(count_copies
const& x
) : tag_(x
.tag_
), id_(++id_count
)
56 trace_op("Copy construct");
59 count_copies(BOOST_RV_REF(count_copies
) x
) : tag_(x
.tag_
), id_(++id_count
)
63 trace_op("Move construct");
66 count_copies
& operator=(
67 BOOST_COPY_ASSIGN_REF(count_copies
) p
) // Copy assignment
71 trace_op("Copy assign");
75 count_copies
& operator=(BOOST_RV_REF(count_copies
) p
) // Move assignment
79 trace_op("Move assign");
83 ~count_copies() { trace_op("Destruct"); }
85 void trace_op(char const* str
)
87 BOOST_LIGHTWEIGHT_TEST_OSTREAM
<< str
<< ": " << tag_
<< " (#" << id_
95 bool operator==(count_copies
const& x
, count_copies
const& y
)
97 return x
.tag_
== y
.tag_
;
100 template <class T
> T
source() { return T(); }
104 count_copies::copies
= 0;
105 count_copies::moves
= 0;
107 BOOST_LIGHTWEIGHT_TEST_OSTREAM
<< "\nReset\n" << std::endl
;
111 #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
114 namespace unnecessary_copy_tests
117 std::size_t hash_value(unnecessary_copy_tests::count_copies
const& x
)
119 return static_cast<std::size_t>(x
.tag_
);
123 // Boost.Move doesn't seem to work very well on this compiler.
128 // It will default construct T, and then move it in.
129 // For 'T const' it seems to copy.
131 #if defined(__IBMCPP__) && __IBMCPP__ <= 1210
132 #define EXTRA_CONSTRUCT_COST 1
134 #define EXTRA_CONSTRUCT_COST 0
137 #define COPY_COUNT(n) \
138 if (::unnecessary_copy_tests::count_copies::copies != n) { \
139 BOOST_ERROR("Wrong number of copies."); \
140 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
141 << "Number of copies: " \
142 << ::unnecessary_copy_tests::count_copies::copies << " expecting: " << n \
145 #define MOVE_COUNT(n) \
146 if (::unnecessary_copy_tests::count_copies::moves != n) { \
147 BOOST_ERROR("Wrong number of moves."); \
148 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
149 << "Number of moves: " << ::unnecessary_copy_tests::count_copies::moves \
150 << " expecting: " << n << std::endl; \
152 #define COPY_COUNT_RANGE(a, b) \
153 if (::unnecessary_copy_tests::count_copies::copies < a || \
154 ::unnecessary_copy_tests::count_copies::copies > b) { \
155 BOOST_ERROR("Wrong number of copies."); \
156 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
157 << "Number of copies: " \
158 << ::unnecessary_copy_tests::count_copies::copies << " expecting: [" \
159 << a << ", " << b << "]" << std::endl; \
161 #define MOVE_COUNT_RANGE(a, b) \
162 if (::unnecessary_copy_tests::count_copies::moves < a || \
163 ::unnecessary_copy_tests::count_copies::moves > b) { \
164 BOOST_ERROR("Wrong number of moves."); \
165 BOOST_LIGHTWEIGHT_TEST_OSTREAM \
166 << "Number of moves: " << ::unnecessary_copy_tests::count_copies::moves \
167 << " expecting: [" << a << ", " << b << "]" << std::endl; \
169 #define COPY_COUNT_EXTRA(a, b) COPY_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
170 #define MOVE_COUNT_EXTRA(a, b) MOVE_COUNT_RANGE(a, a + b * EXTRA_CONSTRUCT_COST)
172 namespace unnecessary_copy_tests
{
173 int count_copies::copies
;
174 int count_copies::moves
;
175 int count_copies::id_count
;
177 template <class T
> void unnecessary_copy_insert_test(T
*)
180 BOOST_DEDUCED_TYPENAME
T::value_type a
;
187 template <class T
> void unnecessary_copy_insert_rvalue_set_test(T
*)
190 BOOST_DEDUCED_TYPENAME
T::value_type a
;
192 x
.insert(boost::move(a
));
196 BOOST_DEDUCED_TYPENAME
T::value_type a2
;
198 x
.insert(boost::move(a
));
200 MOVE_COUNT((x
.size() == 2 ? 1 : 0));
203 template <class T
> void unnecessary_copy_insert_rvalue_map_test(T
*)
205 // Doesn't currently try to emulate std::pair move construction,
206 // so std::pair's require a copy. Could try emulating it in
207 // construct_from_args.
210 BOOST_DEDUCED_TYPENAME
T::value_type a
;
212 x
.insert(boost::move(a
));
213 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
221 BOOST_DEDUCED_TYPENAME
T::value_type a2
;
223 x
.insert(boost::move(a
));
224 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
225 COPY_COUNT((x
.size() == 2 ? 1 : 0));
229 MOVE_COUNT((x
.size() == 2 ? 1 : 0));
233 boost::unordered_set
<count_copies
>* set
;
234 boost::unordered_multiset
<count_copies
>* multiset
;
235 boost::unordered_map
<int, count_copies
>* map
;
236 boost::unordered_multimap
<int, count_copies
>* multimap
;
238 UNORDERED_TEST(unnecessary_copy_insert_test
, ((set
)(multiset
)(map
)(multimap
)))
239 UNORDERED_TEST(unnecessary_copy_insert_rvalue_set_test
, ((set
)(multiset
)))
240 UNORDERED_TEST(unnecessary_copy_insert_rvalue_map_test
, ((map
)(multimap
)))
242 template <class T
> void unnecessary_copy_emplace_test(T
*)
246 BOOST_DEDUCED_TYPENAME
T::value_type a
;
252 template <class T
> void unnecessary_copy_emplace_rvalue_test(T
*)
256 x
.emplace(source
<BOOST_DEDUCED_TYPENAME
T::value_type
>());
257 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
265 unnecessary_copy_emplace_test
, ((set
)(multiset
)(map
)(multimap
)))
267 unnecessary_copy_emplace_rvalue_test
, ((set
)(multiset
)(map
)(multimap
)))
269 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
270 template <class T
> void unnecessary_copy_emplace_std_move_test(T
*)
274 BOOST_DEDUCED_TYPENAME
T::value_type a
;
277 x
.emplace(std::move(a
));
283 unnecessary_copy_emplace_std_move_test
, ((set
)(multiset
)(map
)(multimap
)))
286 template <class T
> void unnecessary_copy_emplace_boost_move_test(T
*)
290 BOOST_DEDUCED_TYPENAME
T::value_type a
;
292 MOVE_COUNT_EXTRA(0, 1);
293 x
.emplace(boost::move(a
));
294 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
298 // Since std::pair isn't movable, move only works for sets.
299 COPY_COUNT_RANGE(1, 2);
300 MOVE_COUNT_RANGE(0, 1);
305 unnecessary_copy_emplace_boost_move_test
, ((set
)(multiset
)(map
)(multimap
)))
307 template <class T
> void unnecessary_copy_emplace_boost_move_set_test(T
*)
311 BOOST_DEDUCED_TYPENAME
T::value_type a
;
314 x
.emplace(boost::move(a
));
320 unnecessary_copy_emplace_boost_move_set_test
, ((set
)(multiset
)))
322 template <class T
> void unnecessary_copy_emplace_boost_move_map_test(T
*)
328 BOOST_DEDUCED_TYPENAME
T::value_type a
;
330 MOVE_COUNT_EXTRA(0, 1);
331 x
.emplace(boost::move(a
));
332 #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
334 MOVE_COUNT_EXTRA(0, 1);
342 unnecessary_copy_emplace_boost_move_map_test
, ((map
)(multimap
)))
344 UNORDERED_AUTO_TEST (unnecessary_copy_emplace_set_test
) {
345 // When calling 'source' the object is moved on some compilers, but not
346 // others. So count that here to adjust later.
349 source
<count_copies
>();
350 int source_cost
= ::unnecessary_copy_tests::count_copies::moves
;
355 boost::unordered_set
<count_copies
> x
;
365 #if !BOOST_UNORDERED_SUN_WORKAROUNDS1
366 // The container will have to create a copy in order to compare with
367 // the existing element.
371 // source_cost doesn't make much sense here, but it seems to fit.
373 MOVE_COUNT(source_cost
);
380 // Emplace should be able to tell that there already is an element
381 // without creating a new one.
387 // A new object is created by source, but it shouldn't be moved or
390 x
.emplace(source
<count_copies
>());
392 MOVE_COUNT(source_cost
);
394 // No move should take place.
396 x
.emplace(boost::move(a
));
400 // Use a new value for cases where a did get moved...
403 // The container will have to create a copy in order to compare with
404 // the existing element.
414 // The container will have to create b copy in order to compare with
415 // the existing element.
417 // Note to self: If copy_count == 0 it's an error not an optimization.
418 // TODO: Devise a better test.
427 UNORDERED_AUTO_TEST (unnecessary_copy_emplace_map_test
) {
428 // When calling 'source' the object is moved on some compilers, but not
429 // others. So count that here to adjust later.
432 source
<count_copies
>();
433 int source_cost
= ::unnecessary_copy_tests::count_copies::moves
;
436 source
<std::pair
<count_copies
, count_copies
> >();
437 int source_pair_cost
= ::unnecessary_copy_tests::count_copies::moves
;
442 boost::unordered_map
<count_copies
, count_copies
> x
;
443 // TODO: Run tests for pairs without const etc.
444 std::pair
<count_copies
const, count_copies
> a
;
446 COPY_COUNT_EXTRA(4, 1);
447 MOVE_COUNT_EXTRA(0, 1);
453 #if !BOOST_UNORDERED_SUN_WORKAROUNDS1
454 // COPY_COUNT(1) would be okay here.
457 #if BOOST_WORKAROUND(BOOST_MSVC, == 1700)
458 // This is a little odd, Visual C++ 11 seems to move the pair, which
459 // results in one copy (for the const key) and one move (for the
460 // non-const mapped value). Since 'emplace(boost::move(a))' (see below)
461 // has the normal result, it must be some odd consequence of how
462 // Visual C++ 11 handles calling move for default arguments.
466 COPY_COUNT_EXTRA(2, 1);
467 MOVE_COUNT_EXTRA(0, 1);
472 x
.emplace(boost::unordered::piecewise_construct
, boost::make_tuple(),
473 boost::make_tuple());
486 // A new object is created by source, but it shouldn't be moved or
489 x
.emplace(source
<std::pair
<count_copies
, count_copies
> >());
491 MOVE_COUNT(source_pair_cost
);
493 #if !(defined(__GNUC__) && __cplusplus < 199900L) && \
494 !(defined(_MSC_VER) && _MSC_VER < 1600)
497 std::pair
<count_copies
const&, count_copies
const&> a_ref(part
, part
);
504 // No move should take place.
505 // (since a is already in the container)
507 x
.emplace(boost::move(a
));
515 std::pair
<count_copies
const, count_copies
> b
;
518 x
.emplace(b
.first
, b
.second
);
523 x
.emplace(source
<count_copies
>(), source
<count_copies
>());
525 MOVE_COUNT(source_cost
* 2);
527 // source<count_copies> creates a single copy.
529 x
.emplace(b
.first
, source
<count_copies
>());
531 MOVE_COUNT(source_cost
);
534 x
.emplace(count_copies(b
.first
.tag_
), count_copies(b
.second
.tag_
));
539 x
.emplace(boost::unordered::piecewise_construct
,
540 boost::make_tuple(boost::ref(b
.first
)),
541 boost::make_tuple(boost::ref(b
.second
)));
545 #if BOOST_UNORDERED_TUPLE_ARGS
548 x
.emplace(boost::unordered::piecewise_construct
,
549 std::make_tuple(std::ref(b
.first
)), std::make_tuple(std::ref(b
.second
)));
553 std::pair
<count_copies
const, count_copies
> move_source_trial
;
555 std::make_tuple(std::move(move_source_trial
.first
));
556 std::make_tuple(std::move(move_source_trial
.second
));
557 int tuple_move_cost
= ::unnecessary_copy_tests::count_copies::moves
;
558 int tuple_copy_cost
= ::unnecessary_copy_tests::count_copies::copies
;
560 std::pair
<count_copies
const, count_copies
> move_source
;
562 x
.emplace(boost::unordered::piecewise_construct
,
563 std::make_tuple(std::move(move_source
.first
)),
564 std::make_tuple(std::move(move_source
.second
)));
565 COPY_COUNT(tuple_copy_cost
);
566 MOVE_COUNT(tuple_move_cost
);
568 #if !defined(BOOST_NO_CXX11_HDR_TUPLE) && \
569 !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ < 6) && \
570 !(defined(BOOST_MSVC) && BOOST_MSVC < 1700)
572 x
.emplace(boost::unordered::piecewise_construct
,
573 std::forward_as_tuple(b
.first
), std::forward_as_tuple(b
.second
));