From 0e61751fab82a177a6deab511e1fb5f150e12534 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 26 Apr 2014 23:24:21 +0200 Subject: [PATCH] Added more tests for move operations, fixed bugs, disabled optional. --- include/boost/optional/optional.hpp | 38 ++++++-- test/Jamfile.v2 | 2 + ...onal_test_fail_copying_a_moveable_type.cpp | 36 ++++++++ ...optional_test_fail_optional_rvalue_ref.cpp | 19 ++++ test/optional_test_move.cpp | 86 +++++++++++++++++-- 5 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 test/optional_test_fail_copying_a_moveable_type.cpp create mode 100644 test/optional_test_fail_optional_rvalue_ref.cpp diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 6933a05..053486e 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -21,13 +21,14 @@ #include #include +#include #include #include #include #include #include #include - #include +#include #include #include #include @@ -139,6 +140,7 @@ struct types_when_isnt_ref typedef T & reference_type ; #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES typedef T && rval_reference_type ; + static rval_reference_type move(reference_type r) { return boost::move(r); } #endif typedef T const* pointer_const_type ; typedef T * pointer_type ; @@ -153,6 +155,7 @@ struct types_when_is_ref typedef raw_type& reference_type ; #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES typedef raw_type&& rval_reference_type ; + static reference_type move(reference_type r) { return r; } #endif typedef raw_type* pointer_const_type ; typedef raw_type* pointer_type ; @@ -310,6 +313,24 @@ class optional_base : public optional_tag construct(rhs.get_impl()); } } + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES + // Assigns from another optional (deep-moves the rhs value) + void assign ( optional_base&& rhs ) + { + if (is_initialized()) + { + if ( rhs.is_initialized() ) + assign_value(boost::move(rhs.get_impl()), is_reference_predicate() ); + else destroy(); + } + else + { + if ( rhs.is_initialized() ) + construct(boost::move(rhs.get_impl())); + } + } +#endif // Assigns from another _convertible_ optional (deep-copies the rhs value) template @@ -328,7 +349,7 @@ class optional_base : public optional_tag } } -#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES // move-assigns from another _convertible_ optional (deep-moves from the rhs value) template void assign ( optional&& rhs ) @@ -420,7 +441,7 @@ class optional_base : public optional_tag #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES void construct ( rval_reference_type val ) { - new (m_storage.address()) internal_type( boost::move(val) ) ; + new (m_storage.address()) internal_type( types::move(val) ) ; m_initialized = true ; } #endif @@ -726,7 +747,6 @@ class optional : public optional_detail::optional_base explicit optional ( Expr&& expr, typename boost::disable_if_c< (boost::is_base_of::type>::value) || - boost::is_same::type, optional>::value || boost::is_same::type, none_t>::value >::type* = 0 ) : base(boost::forward(expr),boost::addressof(expr)) {} @@ -760,7 +780,7 @@ class optional : public optional_detail::optional_base template typename boost::disable_if_c< - boost::is_base_of::type>::value || boost::is_same::type, optional>::value || boost::is_same::type, none_t>::value, + boost::is_base_of::type>::value || boost::is_same::type, none_t>::value, optional& >::type operator= ( Expr&& expr ) @@ -873,6 +893,14 @@ class optional : public optional_detail::optional_base bool operator!() const BOOST_NOEXCEPT { return !this->is_initialized() ; } } ; +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +template +class optional +{ + BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, "Optional rvalue references are illegal."); +} ; +#endif + // Returns optional(v) template inline diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index a146f72..e7e53fa 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -30,5 +30,7 @@ import testing ; [ compile-fail optional_test_ref_fail4.cpp ] [ compile-fail optional_test_inplace_fail.cpp ] [ compile-fail optional_test_inplace_fail2.cpp ] + [ compile-fail optional_test_fail_copying_a_moveable_type.cpp ] + [ compile-fail optional_test_fail_optional_rvalue_ref.cpp ] ; } diff --git a/test/optional_test_fail_copying_a_moveable_type.cpp b/test/optional_test_fail_copying_a_moveable_type.cpp new file mode 100644 index 0000000..c1194cd --- /dev/null +++ b/test/optional_test_fail_copying_a_moveable_type.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2014, Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to 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) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com +// +#include "boost/optional.hpp" + +class MoveOnly +{ +public: + int val; + MoveOnly(int v) : val(v) {} + MoveOnly(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; } + void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; } + +private: + MoveOnly(MoveOnly const&); + void operator=(MoveOnly const&); +}; + +// +// THIS TEST SHOULD FAIL TO COMPILE +// +void test_copying_optional_with_noncopyable_T() +{ + boost::optional opt1 ; + boost::optional opt2(opt1) ; +} + + diff --git a/test/optional_test_fail_optional_rvalue_ref.cpp b/test/optional_test_fail_optional_rvalue_ref.cpp new file mode 100644 index 0000000..5a1e039 --- /dev/null +++ b/test/optional_test_fail_optional_rvalue_ref.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2014, Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to 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) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com +// +#include "boost/optional.hpp" +// +// THIS TEST SHOULD FAIL TO COMPILE +// + +boost::optional oi; + + diff --git a/test/optional_test_move.cpp b/test/optional_test_move.cpp index 0f52c2d..cad5866 100644 --- a/test/optional_test_move.cpp +++ b/test/optional_test_move.cpp @@ -20,6 +20,7 @@ #include "boost/bind/apply.hpp" // Included just to test proper interaction with boost::apply<> as reported by Daniel Wallin #include "boost/mpl/bool.hpp" #include "boost/mpl/bool_fwd.hpp" // For mpl::true_ and mpl::false_ +#include "boost/static_assert.hpp" #include "boost/optional/optional.hpp" @@ -91,7 +92,6 @@ void test_move_ctor_from_U() OracleVal v1; optional o2 (v1); BOOST_CHECK(o2); - std::cout << "AK" << " @@@ " << o2->s << std::endl; BOOST_CHECK(o2->s == sValueCopyConstructed || o2->s == sCopyConstructed || o2->s == sMoveConstructed ); BOOST_CHECK(v1.s == sIntConstructed); @@ -204,7 +204,7 @@ void test_move_assign_from_T() BOOST_CHECK(v1.s == sMovedFrom); } -void test_move_ssign_from_optional_T() +void test_move_assign_from_optional_T() { optional o1; optional o2; @@ -212,8 +212,8 @@ void test_move_ssign_from_optional_T() BOOST_CHECK(!o1); optional o3((Oracle())); o1 = o3; - /*BOOST_CHECK(o3); - BOOST_CHECK(o3->s == sDefaultConstructed); + BOOST_CHECK(o3); + BOOST_CHECK(o3->s == sMoveConstructed); BOOST_CHECK(o1); BOOST_CHECK(o1->s == sCopyConstructed); @@ -225,9 +225,82 @@ void test_move_ssign_from_optional_T() o2 = optional((Oracle())); BOOST_CHECK(o2); - BOOST_CHECK(o2->s == sMoveAssigned);*/ + BOOST_CHECK(o2->s == sMoveAssigned); } +class MoveOnly +{ +public: + int val; + MoveOnly(int v) : val(v) {} + MoveOnly(MoveOnly&& rhs) : val(rhs.val) { rhs.val = 0; } + void operator=(MoveOnly&& rhs) {val = rhs.val; rhs.val = 0; } + +private: + MoveOnly(MoveOnly const&); + void operator=(MoveOnly const&); +}; + +void test_with_move_only() +{ + optional o1; + optional o2((MoveOnly(1))); + BOOST_CHECK(o2); + BOOST_CHECK(o2->val == 1); + optional o3 (boost::move(o1)); + BOOST_CHECK(!o3); + optional o4 (boost::move(o2)); + BOOST_CHECK(o4); + BOOST_CHECK(o4->val == 1); + BOOST_CHECK(o2); + BOOST_CHECK(o2->val == 0); + + o3 = boost::move(o4); + BOOST_CHECK(o3); + BOOST_CHECK(o3->val == 1); + BOOST_CHECK(o4); + BOOST_CHECK(o4->val == 0); +} + +// these 4 classes have different noexcept signatures in move operations +struct NothrowBoth { + NothrowBoth(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {}; + void operator=(NothrowBoth&&) BOOST_NOEXCEPT_IF(true) {}; +}; +struct NothrowCtor { + NothrowCtor(NothrowCtor&&) BOOST_NOEXCEPT_IF(true) {}; + void operator=(NothrowCtor&&) BOOST_NOEXCEPT_IF(false) {}; +}; +struct NothrowAssign { + NothrowAssign(NothrowAssign&&) BOOST_NOEXCEPT_IF(false) {}; + void operator=(NothrowAssign&&) BOOST_NOEXCEPT_IF(true) {}; +}; +struct NothrowNone { + NothrowNone(NothrowNone&&) BOOST_NOEXCEPT_IF(false) {}; + void operator=(NothrowNone&&) BOOST_NOEXCEPT_IF(false) {}; +}; + +#ifndef BOOST_NO_NOEXCEPT + +void test_noexcept() // this is a compile-time test +{ + BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); + + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_constructible >::value); + BOOST_STATIC_ASSERT(!::boost::is_nothrow_move_assignable >::value); + BOOST_STATIC_ASSERT(BOOST_NOEXCEPT_EXPR(optional())); +} +#endif // !defned BOOST_NO_NOEXCEPT #endif @@ -241,7 +314,8 @@ int test_main( int, char* [] ) test_move_ctor_from_optional_T(); test_move_assign_from_U(); test_move_assign_from_T(); - test_move_ssign_from_optional_T(); + test_move_assign_from_optional_T(); + test_with_move_only(); #endif } catch ( ... )