• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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