1 //-----------------------------------------------------------------------------
2 // boost-libs variant/test/variant_nonempty_check.cpp source file
3 // See http://www.boost.org for updates, documentation, and revision history.
4 //-----------------------------------------------------------------------------
6 // Copyright (c) 2014-2019 Antony Polukhin
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)
13 // In this file we are making tests to ensure that variant guarantees nonemptiness.
15 // For that purpose we create a `throwing_class`, that throws exception at a specified
16 // assignment attempt. If exception was thrown during move/assignemnt operation we make sure
17 // that data in variant is same as before move/assignemnt operation or that a fallback type is
20 // Different nonthrowing_class'es are used to tests different variant internal policies:
21 // with/without fallback type + throw/nothrow copyable + throw/nothrow movable
24 #include "boost/variant/variant.hpp"
25 #include "boost/variant/get.hpp"
26 #include "boost/core/lightweight_test.hpp"
29 struct exception_on_assignment
: std::exception
{};
30 struct exception_on_move_assignment
: exception_on_assignment
{};
32 void prevent_compiler_noexcept_detection() {
39 struct throwing_class
{
51 if (trash
< do_not_throw
) {
55 if (trash
> do_not_throw
&& trash
<= throw_after_1
) {
60 return trash
!= do_not_throw
;
63 throwing_class(int value
= 123) BOOST_NOEXCEPT_IF(false) : trash(value
) {
64 prevent_compiler_noexcept_detection();
67 throwing_class(const throwing_class
& b
) BOOST_NOEXCEPT_IF(false) : trash(b
.trash
) {
69 throw exception_on_assignment();
73 const throwing_class
& operator=(const throwing_class
& b
) BOOST_NOEXCEPT_IF(false) {
76 throw exception_on_assignment();
82 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
83 throwing_class(throwing_class
&& b
) BOOST_NOEXCEPT_IF(false) : trash(b
.trash
) {
85 throw exception_on_move_assignment();
89 const throwing_class
& operator=(throwing_class
&& b
) BOOST_NOEXCEPT_IF(false) {
92 throw exception_on_move_assignment();
99 virtual ~throwing_class() {}
102 struct nonthrowing_class
{
105 nonthrowing_class() BOOST_NOEXCEPT_IF(false) : trash(123) {
106 prevent_compiler_noexcept_detection();
109 nonthrowing_class(const nonthrowing_class
&) BOOST_NOEXCEPT_IF(false) {
110 prevent_compiler_noexcept_detection();
113 const nonthrowing_class
& operator=(const nonthrowing_class
&) BOOST_NOEXCEPT_IF(false) {
114 prevent_compiler_noexcept_detection();
118 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
119 nonthrowing_class(nonthrowing_class
&&) BOOST_NOEXCEPT_IF(false) {
120 prevent_compiler_noexcept_detection();
123 const nonthrowing_class
& operator=(nonthrowing_class
&&) BOOST_NOEXCEPT_IF(false) {
124 prevent_compiler_noexcept_detection();
130 struct nonthrowing_class2
{
133 nonthrowing_class2() BOOST_NOEXCEPT_IF(false) : trash(123) {
134 prevent_compiler_noexcept_detection();
138 struct nonthrowing_class3
{
141 nonthrowing_class3() BOOST_NOEXCEPT_IF(true) : trash(123) {}
143 nonthrowing_class3(const nonthrowing_class3
&) BOOST_NOEXCEPT_IF(false) {
144 prevent_compiler_noexcept_detection();
147 const nonthrowing_class3
& operator=(const nonthrowing_class3
&) BOOST_NOEXCEPT_IF(false) {
148 prevent_compiler_noexcept_detection();
152 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
153 nonthrowing_class3(nonthrowing_class3
&&) BOOST_NOEXCEPT_IF(false) {
154 prevent_compiler_noexcept_detection();
157 const nonthrowing_class3
& operator=(nonthrowing_class3
&&) BOOST_NOEXCEPT_IF(false) {
158 prevent_compiler_noexcept_detection();
164 struct nonthrowing_class4
{
167 nonthrowing_class4() BOOST_NOEXCEPT_IF(false) : trash(123) {
168 prevent_compiler_noexcept_detection();
171 nonthrowing_class4(const nonthrowing_class4
&) BOOST_NOEXCEPT_IF(false) {
172 prevent_compiler_noexcept_detection();
175 const nonthrowing_class4
& operator=(const nonthrowing_class4
&) BOOST_NOEXCEPT_IF(false) {
176 prevent_compiler_noexcept_detection();
180 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
181 nonthrowing_class4(nonthrowing_class4
&&) BOOST_NOEXCEPT_IF(true) {
184 const nonthrowing_class4
& operator=(nonthrowing_class4
&&) BOOST_NOEXCEPT_IF(true) {
191 // Tests /////////////////////////////////////////////////////////////////////////////////////
194 template <class Nonthrowing
>
195 inline void check_1_impl(int helper
)
197 boost::variant
<throwing_class
, Nonthrowing
> v
;
199 v
= throwing_class(helper
);
200 BOOST_TEST(!v
.which());
201 BOOST_TEST(boost::get
<throwing_class
>(&v
));
202 } catch (const exception_on_assignment
& /*e*/) {
203 BOOST_TEST(!v
.which());
204 BOOST_TEST(boost::get
<throwing_class
>(&v
));
208 throwing_class
tc(helper
);
210 BOOST_TEST(!v
.which());
211 BOOST_TEST(boost::get
<throwing_class
>(&v
));
212 } catch (const exception_on_assignment
& /*e*/) {
213 BOOST_TEST(!v
.which());
214 BOOST_TEST(boost::get
<throwing_class
>(&v
));
218 inline void check_1(int helper
= 1)
220 check_1_impl
<nonthrowing_class
>(helper
);
221 check_1_impl
<nonthrowing_class2
>(helper
);
222 check_1_impl
<nonthrowing_class3
>(helper
);
223 check_1_impl
<nonthrowing_class4
>(helper
);
224 check_1_impl
<boost::blank
>(helper
);
227 template <class Nonthrowing
>
228 inline void check_2_impl(int helper
)
230 boost::variant
<Nonthrowing
, throwing_class
> v
;
232 v
= throwing_class(helper
);
233 BOOST_TEST(v
.which() == 1);
234 BOOST_TEST(boost::get
<throwing_class
>(&v
));
235 } catch (const exception_on_assignment
& /*e*/) {
236 BOOST_TEST(!v
.which());
237 BOOST_TEST(boost::get
<Nonthrowing
>(&v
));
241 throwing_class
cl(helper
);
243 BOOST_TEST(v
.which() == 1);
244 BOOST_TEST(boost::get
<throwing_class
>(&v
));
245 } catch (const exception_on_assignment
& /*e*/) {
246 BOOST_TEST(!v
.which());
247 BOOST_TEST(boost::get
<Nonthrowing
>(&v
));
251 inline void check_2(int helper
= 1)
253 check_2_impl
<nonthrowing_class
>(helper
);
254 check_2_impl
<nonthrowing_class2
>(helper
);
255 check_2_impl
<nonthrowing_class3
>(helper
);
256 check_2_impl
<nonthrowing_class4
>(helper
);
257 check_2_impl
<boost::blank
>(helper
);
260 template <class Nonthrowing
>
261 inline void check_3_impl(int helper
)
263 boost::variant
<Nonthrowing
, throwing_class
> v1
, v2
;
267 v1
= throwing_class(helper
);
268 BOOST_TEST(v1
.which() == 1);
269 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
270 } catch (const exception_on_assignment
& /*e*/) {
271 BOOST_TEST(!v1
.which());
272 BOOST_TEST(boost::get
<Nonthrowing
>(&v1
));
277 v2
= throwing_class(helper
);
278 BOOST_TEST(v2
.which() == 1);
279 BOOST_TEST(boost::get
<throwing_class
>(&v2
));
280 } catch (const exception_on_assignment
& /*e*/) {
281 BOOST_TEST(!v2
.which());
282 BOOST_TEST(boost::get
<Nonthrowing
>(&v2
));
286 if (!v1
.which() && !v2
.which()) {
287 swap(v1
, v2
); // Make sure that two backup holders swap well
288 BOOST_TEST(!v1
.which());
289 BOOST_TEST(boost::get
<Nonthrowing
>(&v1
));
290 BOOST_TEST(!v2
.which());
291 BOOST_TEST(boost::get
<Nonthrowing
>(&v2
));
297 inline void check_3(int helper
= 1)
299 check_3_impl
<nonthrowing_class
>(helper
);
300 check_3_impl
<nonthrowing_class2
>(helper
);
301 check_3_impl
<nonthrowing_class3
>(helper
);
302 check_3_impl
<nonthrowing_class4
>(helper
);
303 check_3_impl
<boost::blank
>(helper
);
306 inline void check_4(int helper
= 1)
308 // This one has a fallback
309 boost::variant
<int, throwing_class
> v1
, v2
;
313 v1
= throwing_class(helper
);
314 BOOST_TEST(v1
.which() == 1);
315 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
316 } catch (const exception_on_assignment
& /*e*/) {
317 BOOST_TEST(!v1
.which());
318 BOOST_TEST(boost::get
<int>(&v1
));
323 v2
= throwing_class(helper
);
324 BOOST_TEST(v2
.which() == 1);
325 BOOST_TEST(boost::get
<throwing_class
>(&v2
));
326 } catch (const exception_on_assignment
& /*e*/) {
327 BOOST_TEST(!v2
.which());
328 BOOST_TEST(boost::get
<int>(&v2
));
331 if (!v1
.which() && !v2
.which()) {
333 BOOST_TEST(!v1
.which());
334 BOOST_TEST(boost::get
<int>(&v1
));
335 BOOST_TEST(!v2
.which());
336 BOOST_TEST(boost::get
<int>(&v2
));
342 template <class Nonthrowing
>
343 inline void check_5_impl(int helper
)
345 boost::variant
<Nonthrowing
, throwing_class
> v1
, v2
;
346 throwing_class throw_not_now
;
347 throw_not_now
.trash
= throwing_class::do_not_throw
;
351 boost::get
<throwing_class
>(v1
).trash
= 1;
352 boost::get
<throwing_class
>(v2
).trash
= 1;
355 v1
= throwing_class(helper
);
356 BOOST_TEST(v1
.which() == 1);
357 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
358 } catch (const exception_on_assignment
& /*e*/) {
359 BOOST_TEST(v1
.which() == 1);
360 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
363 boost::get
<throwing_class
>(v1
).trash
= throwing_class::do_not_throw
;
364 boost::get
<throwing_class
>(v2
).trash
= throwing_class::do_not_throw
;
368 v1
= throwing_class(helper
);
369 BOOST_TEST(v1
.which() == 1);
370 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
371 } catch (const exception_on_assignment
& /*e*/) {
372 BOOST_TEST(v1
.which() == 0);
373 BOOST_TEST(boost::get
<Nonthrowing
>(&v1
));
376 int v1_type
= v1
.which();
377 int v2_type
= v2
.which();
379 swap(v1
, v2
); // Make sure that backup holders swap well
380 BOOST_TEST(v1
.which() == v2_type
);
381 BOOST_TEST(v2
.which() == v1_type
);
382 } catch (const exception_on_assignment
& /*e*/) {
383 BOOST_TEST(v1
.which() == v1_type
);
384 BOOST_TEST(v2
.which() == v2_type
);
389 inline void check_5(int helper
= 1)
391 check_5_impl
<nonthrowing_class
>(helper
);
392 check_5_impl
<nonthrowing_class2
>(helper
);
393 check_5_impl
<nonthrowing_class3
>(helper
);
394 check_5_impl
<nonthrowing_class4
>(helper
);
395 check_5_impl
<boost::blank
>(helper
);
398 template <class Nonthrowing
>
399 inline void check_6_impl(int helper
)
401 boost::variant
<Nonthrowing
, throwing_class
> v1
, v2
;
402 throwing_class throw_not_now
;
403 throw_not_now
.trash
= throwing_class::do_not_throw
;
410 boost::get
<throwing_class
>(v1
).trash
= 1;
411 boost::get
<throwing_class
>(v2
).trash
= 1;
413 v1
= throwing_class(throw_not_now
);
421 BOOST_TEST(v1
.which() == 1);
422 BOOST_TEST(boost::get
<throwing_class
>(&v1
));
423 } catch (const exception_on_assignment
& /*e*/) {
424 BOOST_TEST(v1
.which() == 0);
431 } catch (const exception_on_assignment
& /*e*/) {
432 BOOST_TEST(v2
.which() == 0);
435 // Probably the most significant test:
436 // unsuccessful swap must preserve old values of variant
438 boost::get
<throwing_class
>(v1
).trash
= helper
;
441 } catch (const exception_on_assignment
& /*e*/) {
442 BOOST_TEST(v1
.which() == 1);
443 BOOST_TEST(v2
.which() == 0);
444 BOOST_TEST(boost::get
<throwing_class
>(v1
).trash
== helper
);
449 inline void check_6(int helper
= 1)
451 check_6_impl
<nonthrowing_class
>(helper
);
452 check_6_impl
<nonthrowing_class2
>(helper
);
453 check_6_impl
<nonthrowing_class3
>(helper
);
454 check_6_impl
<nonthrowing_class4
>(helper
);
455 check_6_impl
<boost::blank
>(helper
);
460 // throwing_class::throw_after_1 + 1 => throw on first assignment/construction
461 // throwing_class::throw_after_1 => throw on second assignment/construction
462 // throwing_class::throw_after_2 => throw on third assignment/construction
464 for (int i
= throwing_class::throw_after_1
+ 1; i
!= throwing_class::do_not_throw
; --i
) {
473 return boost::report_errors();