1 //-----------------------------------------------------------------------------
2 // boost-libs variant/test/rvalue_test.cpp source file
3 // See http://www.boost.org for updates, documentation, and revision history.
4 //-----------------------------------------------------------------------------
5 //
6 // Copyright (c) 2012-2020 Antony Polukhin
7 //
8 // Distributed under the Boost Software License, Version 1.0. (See
9 // accompanying file LICENSE_1_0.txt or copy at
10 // http://www.boost.org/LICENSE_1_0.txt)
11
12 #include "boost/config.hpp"
13
14 #include "boost/core/lightweight_test.hpp"
15 #include "boost/variant.hpp"
16 #include "boost/type_traits/is_nothrow_move_assignable.hpp"
17 #include "boost/mpl/bool.hpp"
18
19 #include <boost/blank.hpp>
20 #include <boost/swap.hpp>
21
22 namespace swap_ambiguouty_test_ns {
23 struct A {};
24 struct B {};
25
swap_ambiguouty_test()26 void swap_ambiguouty_test() {
27 // If boost::blank is not used, then it compiles.
28 typedef boost::variant<boost::blank, A, B> Variant;
29 Variant v1, v2;
30 swap(v1, v2);
31 }
32 } // namespace swap_ambiguouty_test_ns
33
34 // Most part of tests from this file require rvalue references support
35
36 class move_copy_conting_class {
37 public:
38 static unsigned int moves_count;
39 static unsigned int copy_count;
40
move_copy_conting_class()41 move_copy_conting_class(){}
move_copy_conting_class(BOOST_RV_REF (move_copy_conting_class))42 move_copy_conting_class(BOOST_RV_REF(move_copy_conting_class) ) {
43 ++ moves_count;
44 }
45
operator =(BOOST_RV_REF (move_copy_conting_class))46 move_copy_conting_class& operator=(BOOST_RV_REF(move_copy_conting_class) ) {
47 ++ moves_count;
48 return *this;
49 }
50
move_copy_conting_class(const move_copy_conting_class &)51 move_copy_conting_class(const move_copy_conting_class&) {
52 ++ copy_count;
53 }
operator =(BOOST_COPY_ASSIGN_REF (move_copy_conting_class))54 move_copy_conting_class& operator=(BOOST_COPY_ASSIGN_REF(move_copy_conting_class) ) {
55 ++ copy_count;
56 return *this;
57 }
58 };
59
60 unsigned int move_copy_conting_class::moves_count = 0;
61 unsigned int move_copy_conting_class::copy_count = 0;
62
63 #ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
64
run()65 void run()
66 {
67 // Making sure that internals of Boost.Move do not interfere with
68 // internals of Boost.Variant and in case of C++03 or C++98 compilation
69 // is still possible.
70 typedef boost::variant<int, move_copy_conting_class> variant_I_type;
71 variant_I_type v1, v2;
72 v1 = move_copy_conting_class();
73 v2 = v1;
74 v2 = boost::move(v1);
75 v1.swap(v2);
76
77 move_copy_conting_class val;
78 v2 = boost::move(val);
79 v2 = 10;
80
81 variant_I_type v3(boost::move(val));
82 variant_I_type v4(boost::move(v1));
83 }
84
run1()85 void run1()
86 {
87 BOOST_TEST(true);
88 }
89
run_move_only()90 void run_move_only()
91 {
92 BOOST_TEST(true);
93 }
94
run_moves_are_noexcept()95 void run_moves_are_noexcept()
96 {
97 BOOST_TEST(true);
98 }
99
100
run_const_rvalues()101 void run_const_rvalues()
102 {
103 BOOST_TEST(true);
104 }
105
106
107 #else
108
109
run()110 void run()
111 {
112 typedef boost::variant<int, move_copy_conting_class> variant_I_type;
113 variant_I_type v1, v2;
114
115 // Assuring that `move_copy_conting_class` was not created
116 BOOST_TEST(move_copy_conting_class::copy_count == 0);
117 BOOST_TEST(move_copy_conting_class::moves_count == 0);
118
119 v1 = move_copy_conting_class();
120 // Assuring that `move_copy_conting_class` was moved at least once
121 BOOST_TEST(move_copy_conting_class::moves_count != 0);
122
123 unsigned int total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
124 move_copy_conting_class var;
125 v1 = 0;
126 move_copy_conting_class::moves_count = 0;
127 move_copy_conting_class::copy_count = 0;
128 v1 = var;
129 // Assuring that move assignment operator moves/copyes value not more times than copy assignment operator
130 BOOST_TEST(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
131
132 move_copy_conting_class::moves_count = 0;
133 move_copy_conting_class::copy_count = 0;
134 v2 = boost::move(v1);
135 // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
136 BOOST_TEST(move_copy_conting_class::moves_count != 0);
137 BOOST_TEST(move_copy_conting_class::copy_count == 0);
138
139 v1 = move_copy_conting_class();
140 move_copy_conting_class::moves_count = 0;
141 move_copy_conting_class::copy_count = 0;
142 v2 = boost::move(v1);
143 // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
144 BOOST_TEST(move_copy_conting_class::moves_count != 0);
145 BOOST_TEST(move_copy_conting_class::copy_count == 0);
146 total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
147 move_copy_conting_class::moves_count = 0;
148 move_copy_conting_class::copy_count = 0;
149 v1 = v2;
150 // Assuring that move assignment operator moves/copyes value not more times than copy assignment operator
151 BOOST_TEST(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
152
153
154 typedef boost::variant<move_copy_conting_class, int> variant_II_type;
155 variant_II_type v3;
156 move_copy_conting_class::moves_count = 0;
157 move_copy_conting_class::copy_count = 0;
158 v1 = boost::move(v3);
159 // Assuring that `move_copy_conting_class` in v3 was moved at least once (v1 and v3 have different types)
160 BOOST_TEST(move_copy_conting_class::moves_count != 0);
161
162 move_copy_conting_class::moves_count = 0;
163 move_copy_conting_class::copy_count = 0;
164 v2 = boost::move(v1);
165 // Assuring that `move_copy_conting_class` in v1 was moved at least once (v1 and v3 have different types)
166 BOOST_TEST(move_copy_conting_class::moves_count != 0);
167
168 move_copy_conting_class::moves_count = 0;
169 move_copy_conting_class::copy_count = 0;
170 variant_I_type v5(boost::move(v1));
171 // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied
172 BOOST_TEST(move_copy_conting_class::moves_count != 0);
173 BOOST_TEST(move_copy_conting_class::copy_count == 0);
174
175 total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count;
176 move_copy_conting_class::moves_count = 0;
177 move_copy_conting_class::copy_count = 0;
178 variant_I_type v6(v1);
179 // Assuring that move constructor moves/copyes value not more times than copy constructor
180 BOOST_TEST(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count);
181 }
182
run1()183 void run1()
184 {
185 move_copy_conting_class::moves_count = 0;
186 move_copy_conting_class::copy_count = 0;
187
188 move_copy_conting_class c1;
189 typedef boost::variant<int, move_copy_conting_class> variant_I_type;
190 variant_I_type v1(boost::move(c1));
191
192 // Assuring that `move_copy_conting_class` was not copyied
193 BOOST_TEST(move_copy_conting_class::copy_count == 0);
194 BOOST_TEST(move_copy_conting_class::moves_count > 0);
195 }
196
197 struct move_only_structure {
move_only_structuremove_only_structure198 move_only_structure(){}
move_only_structuremove_only_structure199 move_only_structure(move_only_structure&&){}
operator =move_only_structure200 move_only_structure& operator=(move_only_structure&&) { return *this; }
201
202 private:
203 move_only_structure(const move_only_structure&);
204 move_only_structure& operator=(const move_only_structure&);
205 };
206
207 struct visitor_returning_move_only_type: boost::static_visitor<move_only_structure> {
208 template <class T>
operator ()visitor_returning_move_only_type209 move_only_structure operator()(const T&) const {
210 return move_only_structure();
211 }
212 };
213
run_move_only()214 void run_move_only()
215 {
216 move_only_structure mo;
217 boost::variant<int, move_only_structure > vi, vi2(static_cast<move_only_structure&&>(mo));
218 BOOST_TEST(vi.which() == 0);
219 BOOST_TEST(vi2.which() == 1);
220
221 vi = 10;
222 vi2 = 10;
223 BOOST_TEST(vi.which() == 0);
224 BOOST_TEST(vi2.which() == 0);
225
226 vi = static_cast<move_only_structure&&>(mo);
227 vi2 = static_cast<move_only_structure&&>(mo);
228 BOOST_TEST(vi.which() == 1);
229
230 boost::variant<move_only_structure, int > rvi (1);
231 BOOST_TEST(rvi.which() == 1);
232 rvi = static_cast<move_only_structure&&>(mo);
233 BOOST_TEST(rvi.which() == 0);
234 rvi = 1;
235 BOOST_TEST(rvi.which() == 1);
236 rvi = static_cast<boost::variant<int, move_only_structure >&&>(vi2);
237 BOOST_TEST(rvi.which() == 0);
238
239 move_only_structure from_visitor = boost::apply_visitor(visitor_returning_move_only_type(), vi);
240 (void)from_visitor;
241 }
242
run_moves_are_noexcept()243 void run_moves_are_noexcept() {
244 #if !defined(BOOST_NO_CXX11_NOEXCEPT) && (!defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || __GNUC_MINOR__ >= 8)
245 typedef boost::variant<int, short, double> variant_noexcept_t;
246 BOOST_TEST(boost::is_nothrow_move_assignable<variant_noexcept_t>::value);
247 BOOST_TEST(boost::is_nothrow_move_constructible<variant_noexcept_t>::value);
248
249 typedef boost::variant<int, short, double, move_only_structure> variant_except_t;
250 BOOST_TEST(!boost::is_nothrow_move_assignable<variant_except_t>::value);
251 BOOST_TEST(!boost::is_nothrow_move_constructible<variant_except_t>::value);
252 #endif
253 }
254
get_string()255 inline const std::string get_string() { return "test"; }
get_variant()256 inline const boost::variant<int, std::string> get_variant() { return std::string("test"); }
get_variant2()257 inline const boost::variant<std::string, int> get_variant2() { return std::string("test"); }
258
run_const_rvalues()259 void run_const_rvalues()
260 {
261 typedef boost::variant<int, std::string> variant_t;
262 const variant_t v1(get_string());
263 const variant_t v2(get_variant());
264 const variant_t v3(get_variant2());
265
266 variant_t v4, v5, v6, v7;
267 v4 = get_string();
268 v5 = get_variant();
269 v6 = get_variant2();
270 v7 = boost::move(v1);
271 }
272
273 #endif
274
275 struct nothrow_copyable_throw_movable {
nothrow_copyable_throw_movablenothrow_copyable_throw_movable276 nothrow_copyable_throw_movable(){}
nothrow_copyable_throw_movablenothrow_copyable_throw_movable277 nothrow_copyable_throw_movable(const nothrow_copyable_throw_movable&) BOOST_NOEXCEPT {}
operator =nothrow_copyable_throw_movable278 nothrow_copyable_throw_movable& operator=(const nothrow_copyable_throw_movable&) BOOST_NOEXCEPT { return *this; }
279 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_NOEXCEPT_IFnothrow_copyable_throw_movable280 nothrow_copyable_throw_movable(nothrow_copyable_throw_movable&&) BOOST_NOEXCEPT_IF(false) {}
operator =nothrow_copyable_throw_movable281 nothrow_copyable_throw_movable& operator=(nothrow_copyable_throw_movable&&) BOOST_NOEXCEPT_IF(false) { return *this; }
282 #endif
283 };
284
285 // This test is created to cover the following situation:
286 // https://svn.boost.org/trac/boost/ticket/8772
run_tricky_compilation_test()287 void run_tricky_compilation_test()
288 {
289 boost::variant<int, nothrow_copyable_throw_movable> v;
290 v = nothrow_copyable_throw_movable();
291 }
292
293 template <typename T>
294 struct is_container : boost::mpl::false_ {};
295
296 template <typename T>
297 struct is_container<boost::variant<T> > : is_container<T> {};
298
299 template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
300 struct is_container<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
301 : boost::mpl::bool_<is_container<T0>::value
302 || is_container<boost::variant<BOOST_VARIANT_ENUM_SHIFTED_PARAMS(T)> >::value>
303 {};
304
run_is_container_compilation_test()305 void run_is_container_compilation_test()
306 {
307 BOOST_TEST((!is_container<boost::variant<double, int> >::value));
308 BOOST_TEST((!is_container<boost::variant<double, int, char> >::value));
309 BOOST_TEST((!is_container<boost::variant<double, int, char, float> >::value));
310 }
311
main()312 int main()
313 {
314 swap_ambiguouty_test_ns::swap_ambiguouty_test();
315 run();
316 run1();
317 run_move_only();
318 run_moves_are_noexcept();
319 run_tricky_compilation_test();
320 run_const_rvalues();
321 run_is_container_compilation_test();
322
323 #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ > 6)
324 # ifdef BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
325 BOOST_TEST(false &&
326 "Something wrong with macro definitions. GCC-4.7+ is known to work with variadic templates"
327 );
328 # endif
329 #endif
330
331 return boost::report_errors();
332 }
333