• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //-----------------------------------------------------------------------------
5 //
6 // Copyright (c) 2014-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 
13 // In this file we are making tests to ensure that variant guarantees nonemptiness.
14 //
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
18 // stored in variant.
19 //
20 // Different nonthrowing_class'es are used to tests different variant internal policies:
21 // with/without fallback type + throw/nothrow copyable + throw/nothrow movable
22 
23 
24 #include "boost/variant/variant.hpp"
25 #include "boost/variant/get.hpp"
26 #include "boost/core/lightweight_test.hpp"
27 #include <stdexcept>
28 
29 struct exception_on_assignment : std::exception {};
30 struct exception_on_move_assignment : exception_on_assignment {};
31 
prevent_compiler_noexcept_detection()32 void prevent_compiler_noexcept_detection() {
33     char* p = new char;
34     *p = '\0';
35     delete p;
36 }
37 
38 
39 struct throwing_class {
40     int trash;
41     enum helper_enum {
42         do_not_throw = 780,
43         throw_after_5,
44         throw_after_4,
45         throw_after_3,
46         throw_after_2,
47         throw_after_1
48     };
49 
is_throwthrowing_class50     bool is_throw() {
51         if (trash < do_not_throw) {
52             return true;
53         }
54 
55         if (trash > do_not_throw && trash <= throw_after_1) {
56             ++ trash;
57             return false;
58         }
59 
60         return trash != do_not_throw;
61     }
62 
BOOST_NOEXCEPT_IFthrowing_class63     throwing_class(int value = 123) BOOST_NOEXCEPT_IF(false) : trash(value) {
64         prevent_compiler_noexcept_detection();
65     }
66 
BOOST_NOEXCEPT_IFthrowing_class67     throwing_class(const throwing_class& b) BOOST_NOEXCEPT_IF(false) : trash(b.trash) {
68         if (is_throw()) {
69             throw exception_on_assignment();
70         }
71     }
72 
operator =throwing_class73     const throwing_class& operator=(const throwing_class& b) BOOST_NOEXCEPT_IF(false) {
74         trash = b.trash;
75         if (is_throw()) {
76             throw exception_on_assignment();
77         }
78 
79         return *this;
80     }
81 
82 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_NOEXCEPT_IFthrowing_class83     throwing_class(throwing_class&& b) BOOST_NOEXCEPT_IF(false) : trash(b.trash) {
84         if (is_throw()) {
85             throw exception_on_move_assignment();
86         }
87     }
88 
operator =throwing_class89     const throwing_class& operator=(throwing_class&& b) BOOST_NOEXCEPT_IF(false) {
90         trash = b.trash;
91         if (is_throw()) {
92             throw exception_on_move_assignment();
93         }
94 
95         return *this;
96     }
97 #endif
98 
~throwing_classthrowing_class99     virtual ~throwing_class() {}
100 };
101 
102 struct nonthrowing_class {
103     int trash;
104 
BOOST_NOEXCEPT_IFnonthrowing_class105     nonthrowing_class() BOOST_NOEXCEPT_IF(false) : trash(123) {
106         prevent_compiler_noexcept_detection();
107     }
108 
BOOST_NOEXCEPT_IFnonthrowing_class109     nonthrowing_class(const nonthrowing_class&) BOOST_NOEXCEPT_IF(false) {
110         prevent_compiler_noexcept_detection();
111     }
112 
operator =nonthrowing_class113     const nonthrowing_class& operator=(const nonthrowing_class&) BOOST_NOEXCEPT_IF(false) {
114         prevent_compiler_noexcept_detection();
115         return *this;
116     }
117 
118 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_NOEXCEPT_IFnonthrowing_class119     nonthrowing_class(nonthrowing_class&&) BOOST_NOEXCEPT_IF(false) {
120         prevent_compiler_noexcept_detection();
121     }
122 
operator =nonthrowing_class123     const nonthrowing_class& operator=(nonthrowing_class&&) BOOST_NOEXCEPT_IF(false) {
124         prevent_compiler_noexcept_detection();
125         return *this;
126     }
127 #endif
128 };
129 
130 struct nonthrowing_class2 {
131     int trash;
132 
BOOST_NOEXCEPT_IFnonthrowing_class2133     nonthrowing_class2() BOOST_NOEXCEPT_IF(false) : trash(123) {
134         prevent_compiler_noexcept_detection();
135     }
136 };
137 
138 struct nonthrowing_class3 {
139     int trash;
140 
BOOST_NOEXCEPT_IFnonthrowing_class3141     nonthrowing_class3() BOOST_NOEXCEPT_IF(true) : trash(123) {}
142 
BOOST_NOEXCEPT_IFnonthrowing_class3143     nonthrowing_class3(const nonthrowing_class3&) BOOST_NOEXCEPT_IF(false) {
144         prevent_compiler_noexcept_detection();
145     }
146 
operator =nonthrowing_class3147     const nonthrowing_class3& operator=(const nonthrowing_class3&) BOOST_NOEXCEPT_IF(false) {
148         prevent_compiler_noexcept_detection();
149         return *this;
150     }
151 
152 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_NOEXCEPT_IFnonthrowing_class3153     nonthrowing_class3(nonthrowing_class3&&) BOOST_NOEXCEPT_IF(false) {
154         prevent_compiler_noexcept_detection();
155     }
156 
operator =nonthrowing_class3157     const nonthrowing_class3& operator=(nonthrowing_class3&&) BOOST_NOEXCEPT_IF(false) {
158         prevent_compiler_noexcept_detection();
159         return *this;
160     }
161 #endif
162 };
163 
164 struct nonthrowing_class4 {
165     int trash;
166 
BOOST_NOEXCEPT_IFnonthrowing_class4167     nonthrowing_class4() BOOST_NOEXCEPT_IF(false) : trash(123) {
168         prevent_compiler_noexcept_detection();
169     }
170 
BOOST_NOEXCEPT_IFnonthrowing_class4171     nonthrowing_class4(const nonthrowing_class4&) BOOST_NOEXCEPT_IF(false) {
172         prevent_compiler_noexcept_detection();
173     }
174 
operator =nonthrowing_class4175     const nonthrowing_class4& operator=(const nonthrowing_class4&) BOOST_NOEXCEPT_IF(false) {
176         prevent_compiler_noexcept_detection();
177         return *this;
178     }
179 
180 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_NOEXCEPT_IFnonthrowing_class4181     nonthrowing_class4(nonthrowing_class4&&) BOOST_NOEXCEPT_IF(true) {
182     }
183 
operator =nonthrowing_class4184     const nonthrowing_class4& operator=(nonthrowing_class4&&) BOOST_NOEXCEPT_IF(true) {
185         return *this;
186     }
187 #endif
188 };
189 
190 
191 // Tests /////////////////////////////////////////////////////////////////////////////////////
192 
193 
194 template <class Nonthrowing>
check_1_impl(int helper)195 inline void check_1_impl(int helper)
196 {
197     boost::variant<throwing_class, Nonthrowing> v;
198     try {
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));
205     }
206 
207     try {
208         throwing_class tc(helper);
209         v = tc;
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));
215     }
216 }
217 
check_1(int helper=1)218 inline void check_1(int helper = 1)
219 {
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);
225 }
226 
227 template <class Nonthrowing>
check_2_impl(int helper)228 inline void check_2_impl(int helper)
229 {
230     boost::variant<Nonthrowing, throwing_class> v;
231     try {
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));
238     }
239 
240     try {
241         throwing_class cl(helper);
242         v = cl;
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));
248     }
249 }
250 
check_2(int helper=1)251 inline void check_2(int helper = 1)
252 {
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);
258 }
259 
260 template <class Nonthrowing>
check_3_impl(int helper)261 inline void check_3_impl(int helper)
262 {
263     boost::variant<Nonthrowing, throwing_class> v1, v2;
264 
265     swap(v1, v2);
266     try {
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));
273     }
274 
275 
276     try {
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));
283     }
284 
285 
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));
292 
293         v1 = v2;
294     }
295 }
296 
check_3(int helper=1)297 inline void check_3(int helper = 1)
298 {
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);
304 }
305 
check_4(int helper=1)306 inline void check_4(int helper = 1)
307 {
308     // This one has a fallback
309     boost::variant<int, throwing_class> v1, v2;
310 
311     swap(v1, v2);
312     try {
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));
319     }
320 
321 
322     try {
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));
329     }
330 
331     if (!v1.which() && !v2.which()) {
332         swap(v1, v2);
333         BOOST_TEST(!v1.which());
334         BOOST_TEST(boost::get<int>(&v1));
335         BOOST_TEST(!v2.which());
336         BOOST_TEST(boost::get<int>(&v2));
337 
338         v1 = v2;
339     }
340 }
341 
342 template <class Nonthrowing>
check_5_impl(int helper)343 inline void check_5_impl(int helper)
344 {
345     boost::variant<Nonthrowing, throwing_class> v1, v2;
346     throwing_class throw_not_now;
347     throw_not_now.trash = throwing_class::do_not_throw;
348     v1 = throw_not_now;
349     v2 = throw_not_now;
350 
351     boost::get<throwing_class>(v1).trash = 1;
352     boost::get<throwing_class>(v2).trash = 1;
353 
354     try {
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));
361     }
362 
363     boost::get<throwing_class>(v1).trash = throwing_class::do_not_throw;
364     boost::get<throwing_class>(v2).trash = throwing_class::do_not_throw;
365     v1 = Nonthrowing();
366     v2 = Nonthrowing();
367     try {
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));
374     }
375 
376     int v1_type = v1.which();
377     int v2_type = v2.which();
378     try {
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);
385     }
386 }
387 
388 
check_5(int helper=1)389 inline void check_5(int helper = 1)
390 {
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);
396 }
397 
398 template <class Nonthrowing>
check_6_impl(int helper)399 inline void check_6_impl(int helper)
400 {
401     boost::variant<Nonthrowing, throwing_class> v1, v2;
402     throwing_class throw_not_now;
403     throw_not_now.trash = throwing_class::do_not_throw;
404     v1 = throw_not_now;
405     v2 = throw_not_now;
406 
407     v1 = throw_not_now;
408     v2 = throw_not_now;
409     swap(v1, v2);
410     boost::get<throwing_class>(v1).trash = 1;
411     boost::get<throwing_class>(v2).trash = 1;
412 
413     v1 = throwing_class(throw_not_now);
414     v2 = v1;
415 
416     v1 = Nonthrowing();
417     try {
418         throwing_class tc;
419         tc.trash = helper;
420         v1 = tc;
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);
425     }
426 
427     v2 = Nonthrowing();
428     try {
429         v2 = 2;
430         BOOST_TEST(false);
431     } catch (const exception_on_assignment& /*e*/) {
432         BOOST_TEST(v2.which() == 0);
433     }
434 
435     // Probably the most significant test:
436     // unsuccessful swap must preserve old values of variant
437     v1 = throw_not_now;
438     boost::get<throwing_class>(v1).trash = helper;
439     try {
440         swap(v1, v2);
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);
445     }
446 }
447 
448 
check_6(int helper=1)449 inline void check_6(int helper = 1)
450 {
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);
456 }
457 
main()458 int main()
459 {
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
463     // ...
464     for (int i = throwing_class::throw_after_1 + 1; i != throwing_class::do_not_throw; --i) {
465         check_1(i);
466         check_2(i);
467         check_3(i);
468         check_4(i);
469         check_5(i);
470         check_6(i);
471     }
472 
473     return boost::report_errors();
474 }
475