summaryrefslogtreecommitdiff
path: root/libs/variant/test/variant_nonempty_check.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/variant/test/variant_nonempty_check.cpp')
-rw-r--r--libs/variant/test/variant_nonempty_check.cpp474
1 files changed, 474 insertions, 0 deletions
diff --git a/libs/variant/test/variant_nonempty_check.cpp b/libs/variant/test/variant_nonempty_check.cpp
new file mode 100644
index 000000000..de643b862
--- /dev/null
+++ b/libs/variant/test/variant_nonempty_check.cpp
@@ -0,0 +1,474 @@
+//-----------------------------------------------------------------------------
+// boost-libs variant/test/variant_nonempty_check.cpp source file
+// See http://www.boost.org for updates, documentation, and revision history.
+//-----------------------------------------------------------------------------
+//
+// Copyright (c) 2014 Antony Polukhin
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+
+// In this file we are making tests to ensure that variant guarantees nonemptiness.
+//
+// For that purpose we create a `throwing_class`, that throws exception at a specified
+// assignment attempt. If exception was thrown during move/assignemnt operation we make sure
+// that data in variant is same as before move/assignemnt operation or that a fallback type is
+// stored in variant.
+//
+// Different nonthrowing_class'es are used to tests different variant internal policies:
+// with/without fallback type + throw/nothrow copyable + throw/nothrow movable
+
+
+#include "boost/variant/variant.hpp"
+#include "boost/variant/get.hpp"
+#include "boost/test/minimal.hpp"
+#include <stdexcept>
+
+struct exception_on_assignment : std::exception {};
+struct exception_on_move_assignment : exception_on_assignment {};
+
+void prevent_compiler_noexcept_detection() {
+ char* p = new char;
+ *p = '\0';
+ delete p;
+}
+
+
+struct throwing_class {
+ int trash;
+ enum helper_enum {
+ do_not_throw = 780,
+ throw_after_5,
+ throw_after_4,
+ throw_after_3,
+ throw_after_2,
+ throw_after_1
+ };
+
+ bool is_throw() {
+ if (trash < do_not_throw) {
+ return true;
+ }
+
+ if (trash > do_not_throw && trash <= throw_after_1) {
+ ++ trash;
+ return false;
+ }
+
+ return trash != do_not_throw;
+ }
+
+ throwing_class(int value = 123) BOOST_NOEXCEPT_IF(false) : trash(value) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ throwing_class(const throwing_class& b) BOOST_NOEXCEPT_IF(false) : trash(b.trash) {
+ if (is_throw()) {
+ throw exception_on_assignment();
+ }
+ }
+
+ const throwing_class& operator=(const throwing_class& b) BOOST_NOEXCEPT_IF(false) {
+ trash = b.trash;
+ if (is_throw()) {
+ throw exception_on_assignment();
+ }
+
+ return *this;
+ }
+
+#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
+ throwing_class(throwing_class&& b) BOOST_NOEXCEPT_IF(false) : trash(b.trash) {
+ if (is_throw()) {
+ throw exception_on_move_assignment();
+ }
+ }
+
+ const throwing_class& operator=(throwing_class&& b) BOOST_NOEXCEPT_IF(false) {
+ trash = b.trash;
+ if (is_throw()) {
+ throw exception_on_move_assignment();
+ }
+
+ return *this;
+ }
+#endif
+
+ virtual ~throwing_class() {}
+};
+
+struct nonthrowing_class {
+ int trash;
+
+ nonthrowing_class() BOOST_NOEXCEPT_IF(false) : trash(123) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ nonthrowing_class(const nonthrowing_class&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ const nonthrowing_class& operator=(const nonthrowing_class&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ return *this;
+ }
+
+#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
+ nonthrowing_class(nonthrowing_class&&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ const nonthrowing_class& operator=(nonthrowing_class&&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ return *this;
+ }
+#endif
+};
+
+struct nonthrowing_class2 {
+ int trash;
+
+ nonthrowing_class2() BOOST_NOEXCEPT_IF(false) : trash(123) {
+ prevent_compiler_noexcept_detection();
+ }
+};
+
+struct nonthrowing_class3 {
+ int trash;
+
+ nonthrowing_class3() BOOST_NOEXCEPT_IF(true) : trash(123) {}
+
+ nonthrowing_class3(const nonthrowing_class3&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ const nonthrowing_class3& operator=(const nonthrowing_class3&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ return *this;
+ }
+
+#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
+ nonthrowing_class3(nonthrowing_class3&&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ const nonthrowing_class3& operator=(nonthrowing_class3&&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ return *this;
+ }
+#endif
+};
+
+struct nonthrowing_class4 {
+ int trash;
+
+ nonthrowing_class4() BOOST_NOEXCEPT_IF(false) : trash(123) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ nonthrowing_class4(const nonthrowing_class4&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ }
+
+ const nonthrowing_class4& operator=(const nonthrowing_class4&) BOOST_NOEXCEPT_IF(false) {
+ prevent_compiler_noexcept_detection();
+ return *this;
+ }
+
+#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
+ nonthrowing_class4(nonthrowing_class4&&) BOOST_NOEXCEPT_IF(true) {
+ }
+
+ const nonthrowing_class4& operator=(nonthrowing_class4&&) BOOST_NOEXCEPT_IF(true) {
+ return *this;
+ }
+#endif
+};
+
+
+// Tests /////////////////////////////////////////////////////////////////////////////////////
+
+
+template <class Nonthrowing>
+inline void check_1_impl(int helper)
+{
+ boost::variant<throwing_class, Nonthrowing> v;
+ try {
+ v = throwing_class(helper);
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ }
+
+ try {
+ throwing_class tc(helper);
+ v = tc;
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ }
+}
+
+inline void check_1(int helper = 1)
+{
+ check_1_impl<nonthrowing_class>(helper);
+ check_1_impl<nonthrowing_class2>(helper);
+ check_1_impl<nonthrowing_class3>(helper);
+ check_1_impl<nonthrowing_class4>(helper);
+ check_1_impl<boost::blank>(helper);
+}
+
+template <class Nonthrowing>
+inline void check_2_impl(int helper)
+{
+ boost::variant<Nonthrowing, throwing_class> v;
+ try {
+ v = throwing_class(helper);
+ BOOST_CHECK(v.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v));
+ }
+
+ try {
+ throwing_class cl(helper);
+ v = cl;
+ BOOST_CHECK(v.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v));
+ }
+}
+
+inline void check_2(int helper = 1)
+{
+ check_2_impl<nonthrowing_class>(helper);
+ check_2_impl<nonthrowing_class2>(helper);
+ check_2_impl<nonthrowing_class3>(helper);
+ check_2_impl<nonthrowing_class4>(helper);
+ check_2_impl<boost::blank>(helper);
+}
+
+template <class Nonthrowing>
+inline void check_3_impl(int helper)
+{
+ boost::variant<Nonthrowing, throwing_class> v1, v2;
+
+ swap(v1, v2);
+ try {
+ v1 = throwing_class(helper);
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v1.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v1));
+ }
+
+
+ try {
+ v2 = throwing_class(helper);
+ BOOST_CHECK(v2.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v2));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v2.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v2));
+ }
+
+
+ if (!v1.which() && !v2.which()) {
+ swap(v1, v2); // Make sure that two backup holders swap well
+ BOOST_CHECK(!v1.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v1));
+ BOOST_CHECK(!v2.which());
+ BOOST_CHECK(boost::get<Nonthrowing>(&v2));
+
+ v1 = v2;
+ }
+}
+
+inline void check_3(int helper = 1)
+{
+ check_3_impl<nonthrowing_class>(helper);
+ check_3_impl<nonthrowing_class2>(helper);
+ check_3_impl<nonthrowing_class3>(helper);
+ check_3_impl<nonthrowing_class4>(helper);
+ check_3_impl<boost::blank>(helper);
+}
+
+inline void check_4(int helper = 1)
+{
+ // This one has a fallback
+ boost::variant<int, throwing_class> v1, v2;
+
+ swap(v1, v2);
+ try {
+ v1 = throwing_class(helper);
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v1.which());
+ BOOST_CHECK(boost::get<int>(&v1));
+ }
+
+
+ try {
+ v2 = throwing_class(helper);
+ BOOST_CHECK(v2.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v2));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(!v2.which());
+ BOOST_CHECK(boost::get<int>(&v2));
+ }
+
+ if (!v1.which() && !v2.which()) {
+ swap(v1, v2);
+ BOOST_CHECK(!v1.which());
+ BOOST_CHECK(boost::get<int>(&v1));
+ BOOST_CHECK(!v2.which());
+ BOOST_CHECK(boost::get<int>(&v2));
+
+ v1 = v2;
+ }
+}
+
+template <class Nonthrowing>
+inline void check_5_impl(int helper)
+{
+ boost::variant<Nonthrowing, throwing_class> v1, v2;
+ throwing_class throw_not_now;
+ throw_not_now.trash = throwing_class::do_not_throw;
+ v1 = throw_not_now;
+ v2 = throw_not_now;
+
+ boost::get<throwing_class>(v1).trash = 1;
+ boost::get<throwing_class>(v2).trash = 1;
+
+ try {
+ v1 = throwing_class(helper);
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ }
+
+ boost::get<throwing_class>(v1).trash = throwing_class::do_not_throw;
+ boost::get<throwing_class>(v2).trash = throwing_class::do_not_throw;
+ v1 = Nonthrowing();
+ v2 = Nonthrowing();
+ try {
+ v1 = throwing_class(helper);
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v1.which() == 0);
+ BOOST_CHECK(boost::get<Nonthrowing>(&v1));
+ }
+
+ int v1_type = v1.which();
+ int v2_type = v2.which();
+ try {
+ swap(v1, v2); // Make sure that backup holders swap well
+ BOOST_CHECK(v1.which() == v2_type);
+ BOOST_CHECK(v2.which() == v1_type);
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v1.which() == v1_type);
+ BOOST_CHECK(v2.which() == v2_type);
+ }
+}
+
+
+inline void check_5(int helper = 1)
+{
+ check_5_impl<nonthrowing_class>(helper);
+ check_5_impl<nonthrowing_class2>(helper);
+ check_5_impl<nonthrowing_class3>(helper);
+ check_5_impl<nonthrowing_class4>(helper);
+ check_5_impl<boost::blank>(helper);
+}
+
+template <class Nonthrowing>
+inline void check_6_impl(int helper)
+{
+ boost::variant<Nonthrowing, throwing_class> v1, v2;
+ throwing_class throw_not_now;
+ throw_not_now.trash = throwing_class::do_not_throw;
+ v1 = throw_not_now;
+ v2 = throw_not_now;
+
+ v1 = throw_not_now;
+ v2 = throw_not_now;
+ swap(v1, v2);
+ boost::get<throwing_class>(v1).trash = 1;
+ boost::get<throwing_class>(v2).trash = 1;
+
+ v1 = throwing_class(throw_not_now);
+ v2 = v1;
+
+ v1 = Nonthrowing();
+ try {
+ throwing_class tc;
+ tc.trash = helper;
+ v1 = tc;
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(boost::get<throwing_class>(&v1));
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v1.which() == 0);
+ }
+
+ v2 = Nonthrowing();
+ try {
+ v2 = 2;
+ BOOST_CHECK(false);
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v2.which() == 0);
+ }
+
+ // Probably the most significant test:
+ // unsuccessful swap must preserve old values of vaiant
+ v1 = throw_not_now;
+ boost::get<throwing_class>(v1).trash = helper;
+ try {
+ swap(v1, v2);
+ } catch (const exception_on_assignment& /*e*/) {
+ BOOST_CHECK(v1.which() == 1);
+ BOOST_CHECK(v2.which() == 0);
+ BOOST_CHECK(boost::get<throwing_class>(v1).trash == helper);
+ }
+}
+
+
+inline void check_6(int helper = 1)
+{
+ check_6_impl<nonthrowing_class>(helper);
+ check_6_impl<nonthrowing_class2>(helper);
+ check_6_impl<nonthrowing_class3>(helper);
+ check_6_impl<nonthrowing_class4>(helper);
+ check_6_impl<boost::blank>(helper);
+}
+
+int test_main(int , char* [])
+{
+ // throwing_class::throw_after_1 + 1 => throw on first assignment/construction
+ // throwing_class::throw_after_1 => throw on second assignment/construction
+ // throwing_class::throw_after_2 => throw on third assignment/construction
+ // ...
+ for (int i = throwing_class::throw_after_1 + 1; i != throwing_class::do_not_throw; --i) {
+ check_1(i);
+ check_2(i);
+ check_3(i);
+ check_4(i);
+ check_5(i);
+ check_6(i);
+ }
+
+ return boost::exit_success;
+}