diff --git a/include/boost/variant/detail/backup_holder.hpp b/include/boost/variant/detail/backup_holder.hpp index ed112b8..5da7bdd 100644 --- a/include/boost/variant/detail/backup_holder.hpp +++ b/include/boost/variant/detail/backup_holder.hpp @@ -13,6 +13,7 @@ #ifndef BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP #define BOOST_VARIANT_DETAIL_BACKUP_HOLDER_HPP +#include "boost/config.hpp" #include "boost/assert.hpp" namespace boost { @@ -32,7 +33,7 @@ public: // structors delete backup_; } - explicit backup_holder(T* backup) + explicit backup_holder(T* backup) BOOST_NOEXCEPT : backup_(backup) { } @@ -53,7 +54,7 @@ public: // modifiers return *this; } - void swap(backup_holder& rhs) + void swap(backup_holder& rhs) BOOST_NOEXCEPT { T* tmp = rhs.backup_; rhs.backup_ = this->backup_; @@ -83,7 +84,7 @@ backup_holder::backup_holder(const backup_holder&) } template -void swap(backup_holder& lhs, backup_holder& rhs) +void swap(backup_holder& lhs, backup_holder& rhs) BOOST_NOEXCEPT { lhs.swap(rhs); } diff --git a/include/boost/variant/detail/move.hpp b/include/boost/variant/detail/move.hpp index 572cfbb..ffbb129 100644 --- a/include/boost/variant/detail/move.hpp +++ b/include/boost/variant/detail/move.hpp @@ -82,6 +82,8 @@ public: // metafunction result } // namespace detail +#ifdef BOOST_NO_RVALUE_REFERENCES + template inline typename detail::move_type::type @@ -93,6 +95,12 @@ move(T& source) return move_t(source); } +#else + +using std::move; + +#endif + ////////////////////////////////////////////////////////////////////////// // class template return_t // diff --git a/include/boost/variant/variant.hpp b/include/boost/variant/variant.hpp index 6afd190..614b678 100644 --- a/include/boost/variant/variant.hpp +++ b/include/boost/variant/variant.hpp @@ -51,7 +51,9 @@ #include "boost/type_traits/has_nothrow_copy.hpp" #include "boost/type_traits/is_const.hpp" #include "boost/type_traits/is_same.hpp" +#include "boost/type_traits/is_rvalue_reference.hpp" #include "boost/utility/enable_if.hpp" +#include "boost/utility/declval.hpp" #include "boost/variant/recursive_wrapper_fwd.hpp" #include "boost/variant/static_visitor.hpp" @@ -338,7 +340,7 @@ class known_get public: // visitor interface - T& operator()(T& operand) const + T& operator()(T& operand) const BOOST_NOEXCEPT { return operand; } @@ -397,7 +399,7 @@ private: // representation public: // structors - explicit copy_into(void* storage) + explicit copy_into(void* storage) BOOST_NOEXCEPT : storage_(storage) { } @@ -430,6 +432,46 @@ public: // internal visitor interface }; +/////////////////////////////////////////////////////////////////////////////// +// (detail) class move_into +// +// Internal visitor that moves the value it visits into the given buffer. +// +#ifndef BOOST_NO_RVALUE_REFERENCES +class move_into + : public static_visitor<> +{ +private: // representation + + void* storage_; + +public: // structors + + explicit move_into(void* storage) BOOST_NOEXCEPT + : storage_(storage) + { + } + +public: // internal visitor interface + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(boost::detail::variant::backup_holder& operand, long) const + { + new(storage_) T( ::boost::detail::variant::move(operand.get()) ); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(T& operand, int) const BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(T(boost::declval()))) + { + new(storage_) T(::boost::detail::variant::move(operand)); + BOOST_VARIANT_AUX_RETURN_VOID; + } +}; +#endif + /////////////////////////////////////////////////////////////////////////////// // (detail) class assign_storage // @@ -445,7 +487,7 @@ private: // representation public: // structors - explicit assign_storage(const void* rhs_storage) + explicit assign_storage(const void* rhs_storage) BOOST_NOEXCEPT : rhs_storage_(rhs_storage) { } @@ -487,6 +529,63 @@ public: // internal visitor interfaces }; +/////////////////////////////////////////////////////////////////////////////// +// (detail) class move_storage +// +// Internal visitor that moves the given storage (which must be a +// constructed value of the same type) to the value it visits. +// +struct move_storage + : public static_visitor<> +{ +private: // representation + + void* rhs_storage_; + +public: // structors + + explicit move_storage(void* rhs_storage) BOOST_NOEXCEPT + : rhs_storage_(rhs_storage) + { + } + +public: // internal visitor interfaces + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(backup_holder& lhs_content, long) const + { + lhs_content.get() + = ::boost::detail::variant::move(static_cast* >(rhs_storage_)->get()); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(const backup_holder& lhs_content, long) const + { + lhs_content.get() + = ::boost::detail::variant::move(static_cast* >(rhs_storage_)->get()); + BOOST_VARIANT_AUX_RETURN_VOID; + } + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(T& lhs_content, int) const + { + // NOTE TO USER : + // Compile error here indicates one of variant's bounded types does + // not meet the requirements of the Assignable concept. Thus, + // variant is not Assignable. + // + // Hint: Are any of the bounded types const-qualified or references? + // + lhs_content = ::boost::detail::variant::move(*static_cast(rhs_storage_)); + BOOST_VARIANT_AUX_RETURN_VOID; + } + +}; + /////////////////////////////////////////////////////////////////////////////// // (detail) class direct_assigner // @@ -504,7 +603,7 @@ private: // representation public: // structors - explicit direct_assigner(const T& rhs) + explicit direct_assigner(const T& rhs) BOOST_NOEXCEPT : rhs_(rhs) { } @@ -520,7 +619,7 @@ public: // visitor interface } template - bool operator()(U&) + bool operator()(U&) BOOST_NOEXCEPT { return false; } @@ -559,6 +658,65 @@ private: #endif }; +/////////////////////////////////////////////////////////////////////////////// +// (detail) class direct_mover +// +// Generic static visitor that: if and only if the visited value is of the +// specified type, move assigns the given value to the visited value and returns +// true; else returns false. +// +template +class direct_mover + : public static_visitor +{ +private: // representation + + T& rhs_; + +public: // structors + + explicit direct_mover(T& rhs) BOOST_NOEXCEPT + : rhs_(rhs) + { + } + +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300) + +public: // visitor interface + + bool operator()(T& lhs) + { + lhs = ::boost::detail::variant::move(rhs_); + return true; + } + + template + bool operator()(U&) BOOST_NOEXCEPT + { + return false; + } + +#else // MSVC6 + +public: // visitor interface + + template + bool operator()(U& lhs) + { + // MSVC6 can not use direct_mover class + return direct_assigner(rhs_)(lhs); + } + +#endif // MSVC6 workaround + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) +private: + // silence MSVC warning C4512: assignment operator could not be generated + direct_mover& operator= (direct_mover const&); +#endif +}; + + /////////////////////////////////////////////////////////////////////////////// // (detail) class backup_assigner // @@ -745,7 +903,7 @@ class reflect public: // visitor interfaces template - const std::type_info& operator()(const T&) const + const std::type_info& operator()(const T&) const BOOST_NOEXCEPT { return typeid(T); } @@ -772,7 +930,7 @@ private: // representation public: // structors - explicit comparer(const Variant& lhs) + explicit comparer(const Variant& lhs) BOOST_NOEXCEPT : lhs_(lhs) { } @@ -844,7 +1002,7 @@ public: // visitor typedefs public: // structors - explicit invoke_visitor(Visitor& visitor) + explicit invoke_visitor(Visitor& visitor) BOOST_NOEXCEPT : visitor_(visitor) { } @@ -1170,26 +1328,26 @@ private: // helpers, for representation (below) which_t which_; storage_t storage_; - void indicate_which(int which_arg) + void indicate_which(int which_arg) BOOST_NOEXCEPT { which_ = static_cast( which_arg ); } - void indicate_backup_which(int which_arg) + void indicate_backup_which(int which_arg) BOOST_NOEXCEPT { which_ = static_cast( -(which_arg + 1) ); } private: // helpers, for queries (below) - bool using_backup() const + bool using_backup() const BOOST_NOEXCEPT { return which_ < 0; } public: // queries - int which() const + int which() const BOOST_NOEXCEPT { // If using heap backup... if (using_backup()) @@ -1222,7 +1380,7 @@ public: // structors destroy_content(); } - variant() + variant() BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor::type::value) { // NOTE TO USER : // Compile error from here indicates that the first bound @@ -1244,7 +1402,7 @@ private: // helpers, for structors, cont. (below) public: // structors - explicit convert_copy_into(void* storage) + explicit convert_copy_into(void* storage) BOOST_NOEXCEPT : storage_(storage) { } @@ -1454,6 +1612,18 @@ public: // structors, cont. // ...and activate the *this's primary storage on success: indicate_which(operand.which()); } + +#ifndef BOOST_NO_RVALUE_REFERENCES + variant(variant&& operand) + { + // Move the value of operand into *this... + detail::variant::move_into visitor( storage_.address() ); + operand.internal_apply_visitor(visitor); + + // ...and activate the *this's primary storage on success: + indicate_which(operand.which()); + } +#endif private: // helpers, for modifiers (below) @@ -1478,7 +1648,7 @@ private: // helpers, for modifiers (below) public: // structors - assigner(variant& lhs, int rhs_which) + assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT : lhs_(lhs) , rhs_which_(rhs_which) { @@ -1605,8 +1775,153 @@ private: // helpers, for modifiers (below) assigner& operator= (assigner const&); #endif }; - + friend class assigner; + +#ifndef BOOST_NO_RVALUE_REFERENCES + // class move_assigner + // + // Internal visitor that "move assigns" the visited value to the given variant + // by appropriate destruction and move-construction. + // + + class move_assigner + : public static_visitor<> + { + private: // representation + + variant& lhs_; + int rhs_which_; + + public: // structors + + move_assigner(variant& lhs, int rhs_which) BOOST_NOEXCEPT + : lhs_(lhs) + , rhs_which_(rhs_which) + { + } + + private: // helpers, for internal visitor interface (below) + + template + void assign_impl( + RhsT& rhs_content + , mpl::true_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , B2// has_fallback_type + ) + { + // Destroy lhs's content... + lhs_.destroy_content(); // nothrow + + // ...copy rhs content into lhs's storage... + new(lhs_.storage_.address()) + RhsT( rhs_content ); // nothrow + + // ...and indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + RhsT& rhs_content + , mpl::true_// has_nothrow_copy + , mpl::true_// has_nothrow_move_constructor + , B// has_fallback_type + ) + { + // ...destroy lhs's content... + lhs_.destroy_content(); // nothrow + + // ...move the rhs_content into lhs's storage... + new(lhs_.storage_.address()) + RhsT( detail::variant::move(rhs_content) ); // nothrow + + // ...and indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + RhsT& rhs_content + , mpl::false_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , mpl::true_// has_fallback_type + ) + { + // Destroy lhs's content... + lhs_.destroy_content(); // nothrow + + try + { + // ...and attempt to copy rhs's content into lhs's storage: + new(lhs_.storage_.address()) + RhsT( detail::variant::move(rhs_content) ); + } + catch (...) + { + // In case of failure, default-construct fallback type in lhs's storage... + new (lhs_.storage_.address()) + fallback_type_; // nothrow + + // ...indicate construction of fallback type... + lhs_.indicate_which( + BOOST_MPL_AUX_VALUE_WKND(fallback_type_index_)::value + ); // nothrow + + // ...and rethrow: + throw; + } + + // In the event of success, indicate new content type: + lhs_.indicate_which(rhs_which_); // nothrow + } + + template + void assign_impl( + const RhsT& rhs_content + , mpl::false_// has_nothrow_copy + , mpl::false_// has_nothrow_move_constructor + , mpl::false_// has_fallback_type + ) + { + detail::variant::backup_assigner + visitor(lhs_, rhs_which_, rhs_content); + lhs_.internal_apply_visitor(visitor); + } + + public: // internal visitor interfaces + + template + BOOST_VARIANT_AUX_RETURN_VOID_TYPE + internal_visit(RhsT& rhs_content, int) + { + typedef typename detail::variant::has_nothrow_move_constructor::type + nothrow_move_constructor; + typedef typename mpl::or_< // reduces compile-time + nothrow_move_constructor + , has_nothrow_copy + >::type nothrow_copy; + + assign_impl( + rhs_content + , nothrow_copy() + , nothrow_move_constructor() + , has_fallback_type_() + ); + + BOOST_VARIANT_AUX_RETURN_VOID; + } + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) + private: + // silence MSVC warning C4512: assignment operator could not be generated + move_assigner& operator= (move_assigner const&); +#endif + }; + + friend class move_assigner; +#endif // BOOST_NO_RVALUE_REFERENCES void variant_assign(const variant& rhs) { @@ -1625,6 +1940,25 @@ private: // helpers, for modifiers (below) } } +#ifndef BOOST_NO_RVALUE_REFERENCES + void variant_assign(variant&& rhs) + { + // If the contained types are EXACTLY the same... + if (which_ == rhs.which_) + { + // ...then move rhs's storage to lhs's content: + detail::variant::move_storage visitor(rhs.storage_.address()); + this->internal_apply_visitor(visitor); + } + else + { + // Otherwise, perform general (move-based) variant assignment: + move_assigner visitor(*this, rhs.which()); + rhs.internal_apply_visitor(visitor); + } + } +#endif // BOOST_NO_RVALUE_REFERENCES + private: // helpers, for modifiers (below) template @@ -1645,8 +1979,37 @@ private: // helpers, for modifiers (below) } } +#ifndef BOOST_NO_RVALUE_REFERENCES + template + void move_assign(T&& rhs) + { + // If direct T-to-T move assignment is not possible... + detail::variant::direct_mover direct_move(rhs); + if (this->apply_visitor(direct_move) == false) + { + // ...then convert rhs to variant and assign: + // + // While potentially inefficient, the following construction of a + // variant allows T as any type convertible to one of the bounded + // types without excessive code redundancy. + // + variant temp( detail::variant::move(rhs) ); + variant_assign( detail::variant::move(temp) ); + } + } +#endif // BOOST_NO_RVALUE_REFERENCES + public: // modifiers +#ifndef BOOST_NO_RVALUE_REFERENCES + template + typename boost::enable_if, variant& >::type operator=(T&& rhs) + { + move_assign( detail::variant::move(rhs) ); + return *this; + } +#endif // BOOST_NO_RVALUE_REFERENCES + template variant& operator=(const T& rhs) { @@ -1661,6 +2024,14 @@ public: // modifiers return *this; } +#ifndef BOOST_NO_RVALUE_REFERENCES + variant& operator=(variant&& rhs) + { + variant_assign( detail::variant::move(rhs) ); + return *this; + } +#endif // BOOST_NO_RVALUE_REFERENCES + void swap(variant& rhs) { // If the contained types are the same... @@ -1685,7 +2056,7 @@ public: // queries // NOTE: member which() defined above. // - bool empty() const + bool empty() const BOOST_NOEXCEPT { return false; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 456f108..aba9a8c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -33,6 +33,7 @@ test-suite variant [ run variant_comparison_test.cpp ] [ run variant_visit_test.cpp ] [ run hash_variant_test.cpp ] + [ run rvalue_test.cpp ] ; diff --git a/test/rvalue_test.cpp b/test/rvalue_test.cpp new file mode 100644 index 0000000..cb697c7 --- /dev/null +++ b/test/rvalue_test.cpp @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// boost-libs variant/test/rvalue_test.cpp source file +// See http://www.boost.org for updates, documentation, and revision history. +//----------------------------------------------------------------------------- +// +// Copyright (c) 2012 +// 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) + +#include "boost/config.hpp" + +#include "boost/test/minimal.hpp" +#include "boost/variant.hpp" + +// This test requires BOOST_HAS_RVALUE_REFS + +#ifndef BOOST_HAS_RVALUE_REFS + +void run() +{ + BOOST_CHECK(true); +} + +#else + +class move_copy_conting_class { +public: + static unsigned int moves_count; + static unsigned int copy_count; + + move_copy_conting_class(){} + move_copy_conting_class(move_copy_conting_class&&) { + ++ moves_count; + } + + move_copy_conting_class& operator=(move_copy_conting_class&&) { + ++ moves_count; + return *this; + } + + move_copy_conting_class(const move_copy_conting_class&) { + ++ copy_count; + } + move_copy_conting_class& operator=(const move_copy_conting_class&) { + ++ copy_count; + return *this; + } +}; + +unsigned int move_copy_conting_class::moves_count = 0; +unsigned int move_copy_conting_class::copy_count = 0; + +void run() +{ + typedef boost::variant variant_I_type; + variant_I_type v1, v2; + + // Assuring that `move_copy_conting_class` was not created + BOOST_CHECK(move_copy_conting_class::copy_count == 0); + BOOST_CHECK(move_copy_conting_class::moves_count == 0); + + v1 = move_copy_conting_class(); + // Assuring that `move_copy_conting_class` was moved at least once + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + + unsigned int total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count; + move_copy_conting_class var; + v1 = 0; + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v1 = var; + // Assuring that move assignment operator moves/copyes value not more times than copy assignment operator + BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count); + + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v2 = static_cast(v1); + // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + BOOST_CHECK(move_copy_conting_class::copy_count == 0); + + v1 = move_copy_conting_class(); + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v2 = static_cast(v1); + // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + BOOST_CHECK(move_copy_conting_class::copy_count == 0); + total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count; + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v1 = v2; + // Assuring that move assignment operator moves/copyes value not more times than copy assignment operator + BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count); + + + typedef boost::variant variant_II_type; + variant_II_type v3; + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v1 = static_cast(v3); + // Assuring that `move_copy_conting_class` in v3 was moved at least once (v1 and v3 have different types) + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + v2 = static_cast(v1); + // Assuring that `move_copy_conting_class` in v1 was moved at least once (v1 and v3 have different types) + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + variant_I_type v5(static_cast(v1)); + // Assuring that `move_copy_conting_class` in v1 was moved at least once and was not copied + BOOST_CHECK(move_copy_conting_class::moves_count != 0); + BOOST_CHECK(move_copy_conting_class::copy_count == 0); + + total_count = move_copy_conting_class::moves_count + move_copy_conting_class::copy_count; + move_copy_conting_class::moves_count = 0; + move_copy_conting_class::copy_count = 0; + variant_I_type v6(v1); + // Assuring that move constructor moves/copyes value not more times than copy constructor + BOOST_CHECK(total_count <= move_copy_conting_class::moves_count + move_copy_conting_class::copy_count); +} + + +#endif + + +int test_main(int , char* []) +{ + run(); + return 0; +} \ No newline at end of file