diff --git a/doc/move.qbk b/doc/move.qbk index 0d47f52..372b374 100644 --- a/doc/move.qbk +++ b/doc/move.qbk @@ -764,7 +764,8 @@ Many thanks to all boosters that have tested, reviewed and improved the library. [section:release_notes_boost_1_58_00 Boost 1.58 Release] -* Added `BOOST_MOVE_BASE` utility. +* Added [macroref BOOST_MOVE_BASE BOOST_MOVE_BASE] utility. +* Added [funcref boost::adl_move_swap adl_move_swap] utility. [endsect] diff --git a/include/boost/move/adl_move_swap.hpp b/include/boost/move/adl_move_swap.hpp index c2c92dc..a84b883 100644 --- a/include/boost/move/adl_move_swap.hpp +++ b/include/boost/move/adl_move_swap.hpp @@ -43,45 +43,181 @@ #include //for boost::move +#if !defined(BOOST_MOVE_DOXYGEN_INVOKED) + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +namespace boost_move_member_swap { + +struct dont_care +{ + dont_care(...); +}; + +struct private_type +{ + static private_type p; + private_type const &operator,(int) const; +}; + +typedef char yes_type; +struct no_type{ char dummy[2]; }; + +template +no_type is_private_type(T const &); + +yes_type is_private_type(private_type const &); + +template +class has_member_function_named_swap +{ + struct BaseMixin + { + void swap(); + }; + + struct Base : public Type, public BaseMixin { Base(); }; + template class Helper{}; + + template + static no_type deduce(U*, Helper* = 0); + static yes_type deduce(...); + + public: + static const bool value = sizeof(yes_type) == sizeof(deduce((Base*)(0))); +}; + +template +struct has_member_swap_impl +{ + static const bool value = false; +}; + +template +struct has_member_swap_impl +{ + struct FunWrap : Fun + { + FunWrap(); + + using Fun::swap; + private_type swap(dont_care) const; + }; + + static Fun &declval_fun(); + static FunWrap declval_wrap(); + + static bool const value = + sizeof(no_type) == sizeof(is_private_type( (declval_wrap().swap(declval_fun()), 0)) ); +}; + +template +struct has_member_swap : public has_member_swap_impl + ::value> +{}; + +} //namespace boost_move_member_swap + namespace boost_move_adl_swap{ +template +struct and_op_impl +{ static const bool value = false; }; + +template +struct and_op_impl +{ static const bool value = P2::value; }; + +template +struct and_op + : and_op_impl +{}; + +////// + +template +struct and_op_not_impl +{ static const bool value = false; }; + +template +struct and_op_not_impl +{ static const bool value = !P2::value; }; + +template +struct and_op_not + : and_op_not_impl +{}; + template -void swap_proxy(T& left, T& right, typename boost::move_detail::enable_if_c::value>::type* = 0) +void swap_proxy(T& x, T& y, typename boost::move_detail::enable_if_c::value>::type* = 0) { //use std::swap if argument dependent lookup fails //Use using directive ("using namespace xxx;") instead as some older compilers //don't do ADL with using declarations ("using ns::func;"). using namespace std; - swap(left, right); + swap(x, y); } template -void swap_proxy(T& left, T& right, typename boost::move_detail::enable_if_c::value>::type* = 0) +void swap_proxy(T& x, T& y + , typename boost::move_detail::enable_if< and_op_not_impl + , boost_move_member_swap::has_member_swap > + >::type* = 0) +{ T t(::boost::move(x)); x = ::boost::move(y); y = ::boost::move(t); } + +template +void swap_proxy(T& x, T& y + , typename boost::move_detail::enable_if< and_op_impl< boost::move_detail::has_move_emulation_enabled_impl + , boost_move_member_swap::has_member_swap > + >::type* = 0) +{ x.swap(y); } + +} //namespace boost_move_adl_swap{ + +#else + +namespace boost_move_adl_swap{ + +template +void swap_proxy(T& x, T& y) { - T tmp(::boost::move(left)); - left = ::boost::move(right); - right = ::boost::move(tmp); + using std::swap; + swap(x, y); } +} //namespace boost_move_adl_swap{ + +#endif //#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + +namespace boost_move_adl_swap{ + template -void swap_proxy(T (& left)[N], T (& right)[N]) +void swap_proxy(T (& x)[N], T (& y)[N]) { for (std::size_t i = 0; i < N; ++i){ - ::boost_move_adl_swap::swap_proxy(left[i], right[i]); + ::boost_move_adl_swap::swap_proxy(x[i], y[i]); } } } //namespace boost_move_adl_swap { +#endif //!defined(BOOST_MOVE_DOXYGEN_INVOKED) + namespace boost{ -// adl_move_swap has two template arguments, instead of one, to -// avoid ambiguity when swapping objects of a Boost type that does -// not have its own boost::swap overload. +//! Exchanges the values of a and b, using Argument Dependent Lookup (ADL) to select a +//! specialized swap function if available. If no specialized swap function is available, +//! std::swap is used. +//! +//! Exception: If T uses Boost.Move's move emulation and the compiler has +//! no rvalue references then: +//! +//! - If T has a T::swap(T&) member, that member is called. +//! - Otherwise a move-based swap is called, equivalent to: +//! T t(::boost::move(x)); x = ::boost::move(y); y = ::boost::move(t);. template -void adl_move_swap(T& left, T& right) +void adl_move_swap(T& x, T& y) { - ::boost_move_adl_swap::swap_proxy(left, right); + ::boost_move_adl_swap::swap_proxy(x, y); } } //namespace boost{ diff --git a/proj/vc7ide/Move.sln b/proj/vc7ide/Move.sln index bb3bfb9..8429ad4 100644 --- a/proj/vc7ide/Move.sln +++ b/proj/vc7ide/Move.sln @@ -103,6 +103,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unique_ptr_types_test", "un ProjectSection(ProjectDependencies) = postProject EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adl_move_swap", "adl_move_swap.vcproj", "{CD2617A8-79EB-6172-2CE4-26617AA3AC93}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -215,6 +219,10 @@ Global {C57C28A3-4FE0-6208-BF87-B2B61D3A7675}.Debug.Build.0 = Debug|Win32 {C57C28A3-4FE0-6208-BF87-B2B61D3A7675}.Release.ActiveCfg = Release|Win32 {C57C28A3-4FE0-6208-BF87-B2B61D3A7675}.Release.Build.0 = Release|Win32 + {CD2617A8-79EB-6172-2CE4-26617AA3AC93}.Debug.ActiveCfg = Debug|Win32 + {CD2617A8-79EB-6172-2CE4-26617AA3AC93}.Debug.Build.0 = Debug|Win32 + {CD2617A8-79EB-6172-2CE4-26617AA3AC93}.Release.ActiveCfg = Release|Win32 + {CD2617A8-79EB-6172-2CE4-26617AA3AC93}.Release.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionItems) = postSolution ..\..\..\..\boost\move\adl_move_swap.hpp = ..\..\..\..\boost\move\adl_move_swap.hpp diff --git a/proj/vc7ide/adl_move_swap.vcproj b/proj/vc7ide/adl_move_swap.vcproj new file mode 100644 index 0000000..4c57491 --- /dev/null +++ b/proj/vc7ide/adl_move_swap.vcproj @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/adl_move_swap.cpp b/test/adl_move_swap.cpp new file mode 100644 index 0000000..863e893 --- /dev/null +++ b/test/adl_move_swap.cpp @@ -0,0 +1,268 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2014-2014. +// 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) +// +// See http://www.boost.org/libs/move for documentation. +// +////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +class swap_stats +{ + public: + static void reset_stats() + { + member_swap_calls = 0; + friend_swap_calls = 0; + move_cnstor_calls = 0; + move_assign_calls = 0; + copy_cnstor_calls = 0; + copy_assign_calls = 0; + } + + static unsigned int member_swap_calls; + static unsigned int friend_swap_calls; + static unsigned int move_cnstor_calls; + static unsigned int move_assign_calls; + static unsigned int copy_cnstor_calls; + static unsigned int copy_assign_calls; +}; + +unsigned int swap_stats::member_swap_calls = 0; +unsigned int swap_stats::friend_swap_calls = 0; +unsigned int swap_stats::move_cnstor_calls = 0; +unsigned int swap_stats::move_assign_calls = 0; +unsigned int swap_stats::copy_cnstor_calls = 0; +unsigned int swap_stats::copy_assign_calls = 0; + +class movable : public swap_stats +{ + BOOST_MOVABLE_BUT_NOT_COPYABLE(movable) + public: + movable() {} + movable(BOOST_RV_REF(movable)) { ++move_cnstor_calls; } + movable & operator=(BOOST_RV_REF(movable)){ ++move_assign_calls; return *this; } + friend void swap(movable &, movable &) { ++friend_swap_calls; } +}; + +class movable_swap_member : public swap_stats +{ + BOOST_MOVABLE_BUT_NOT_COPYABLE(movable_swap_member) + public: + movable_swap_member() {} + movable_swap_member(BOOST_RV_REF(movable_swap_member)) { ++move_cnstor_calls; } + movable_swap_member & operator=(BOOST_RV_REF(movable_swap_member)){ ++move_assign_calls; return *this; } + void swap(movable_swap_member &) { ++member_swap_calls; } + friend void swap(movable_swap_member &, movable_swap_member &) { ++friend_swap_calls; } +}; + +class copyable : public swap_stats +{ + public: + copyable() {} + copyable(const copyable &) { ++copy_cnstor_calls; } + copyable & operator=(const copyable&) { ++copy_assign_calls; return *this; } + void swap(copyable &) { ++member_swap_calls; } + friend void swap(copyable &, copyable &) { ++friend_swap_calls; } +}; + +class no_swap : public swap_stats +{ + private: unsigned m_state; + public: + explicit no_swap(unsigned i): m_state(i){} + no_swap(const no_swap &x) { m_state = x.m_state; ++copy_cnstor_calls; } + no_swap & operator=(const no_swap& x) { m_state = x.m_state; ++copy_assign_calls; return *this; } + void swap(no_swap &) { ++member_swap_calls; } + friend bool operator==(const no_swap &x, const no_swap &y) { return x.m_state == y.m_state; } + friend bool operator!=(const no_swap &x, const no_swap &y) { return !(x==y); } +}; + + +int main() +{ + { //movable + movable x, y; + swap_stats::reset_stats(); + ::boost::adl_move_swap(x, y); + #if defined(BOOST_NO_RVALUE_REFERENCES) + //In non rvalue reference compilers, + //movable classes with no swap() member uses + //boost::move() to implement swap. + BOOST_TEST(swap_stats::friend_swap_calls == 0); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::move_cnstor_calls == 1); + BOOST_TEST(swap_stats::move_assign_calls == 2); + BOOST_TEST(swap_stats::copy_cnstor_calls == 0); + BOOST_TEST(swap_stats::copy_assign_calls == 0); + #else + //In compilers with rvalue references, this should call friend swap via ADL + BOOST_TEST(swap_stats::friend_swap_calls == 1); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::move_cnstor_calls == 0); + BOOST_TEST(swap_stats::move_assign_calls == 0); + BOOST_TEST(swap_stats::copy_cnstor_calls == 0); + BOOST_TEST(swap_stats::copy_assign_calls == 0); + #endif + } + { //movable_swap_member + movable_swap_member x, y; + swap_stats::reset_stats(); + ::boost::adl_move_swap(x, y); + #if defined(BOOST_NO_RVALUE_REFERENCES) + //In non rvalue reference compilers, + //movable classes with no swap() member uses + //boost::move() to implement swap. + BOOST_TEST(swap_stats::friend_swap_calls == 0); + BOOST_TEST(swap_stats::member_swap_calls == 1); + BOOST_TEST(swap_stats::move_cnstor_calls == 0); + BOOST_TEST(swap_stats::move_assign_calls == 0); + BOOST_TEST(swap_stats::copy_cnstor_calls == 0); + BOOST_TEST(swap_stats::copy_assign_calls == 0); + #else + //In compilers with rvalue references, this should call friend swap via ADL + BOOST_TEST(swap_stats::friend_swap_calls == 1); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::move_cnstor_calls == 0); + BOOST_TEST(swap_stats::move_assign_calls == 0); + BOOST_TEST(swap_stats::copy_cnstor_calls == 0); + BOOST_TEST(swap_stats::copy_assign_calls == 0); + #endif + } + { //copyable + copyable x, y; + swap_stats::reset_stats(); + ::boost::adl_move_swap(x, y); + //This should call friend swap via ADL + BOOST_TEST(swap_stats::friend_swap_calls == 1); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::move_cnstor_calls == 0); + BOOST_TEST(swap_stats::move_assign_calls == 0); + BOOST_TEST(swap_stats::copy_cnstor_calls == 0); + BOOST_TEST(swap_stats::copy_assign_calls == 0); + } + { //no_swap + no_swap x(1), y(2), x_back(x), y_back(y); + swap_stats::reset_stats(); + ::boost::adl_move_swap(x, y); + //This should call std::swap which uses copies + BOOST_TEST(swap_stats::friend_swap_calls == 0); + BOOST_TEST(swap_stats::member_swap_calls == 0); + BOOST_TEST(swap_stats::move_cnstor_calls == 0); + BOOST_TEST(swap_stats::move_assign_calls == 0); + BOOST_TEST(swap_stats::copy_cnstor_calls == 1); + BOOST_TEST(swap_stats::copy_assign_calls == 2); + BOOST_TEST(x == y_back); + BOOST_TEST(y == x_back); + BOOST_TEST(x != y); + } + return ::boost::report_errors(); +} +#include + +/* +#include + +namespace boost_move_member_swap { + +struct dont_care +{ + dont_care(...); +}; + +struct private_type +{ + static private_type p; + private_type const &operator,(int) const; +}; + +typedef char yes_type; +struct no_type{ char dummy[2]; }; + +template +no_type is_private_type(T const &); + +yes_type is_private_type(private_type const &); + +template +class has_member_function_named_swap +{ + struct BaseMixin + { + void swap(); + }; + + struct Base : public Type, public BaseMixin { Base(); }; + template class Helper{}; + + template + static no_type deduce(U*, Helper* = 0); + static yes_type deduce(...); + + public: + static const bool value = sizeof(yes_type) == sizeof(deduce((Base*)(0))); +}; + +template +struct has_member_swap_impl +{ + static const bool value = false; +}; + +template +struct has_member_swap_impl +{ + struct FunWrap : Fun + { + FunWrap(); + + using Fun::swap; + private_type swap(dont_care) const; + }; + + static Fun &declval_fun(); + static FunWrap declval_wrap(); + + static bool const value = + sizeof(no_type) == sizeof(is_private_type( (declval_wrap().swap(declval_fun()), 0)) ); +}; + +template +struct has_member_swap : public has_member_swap_impl + ::value> +{}; + +} //namespace boost_move_member_swap + +class noswap +{ + public: + void swap(); +}; + +class noneswap +{ +}; + +class yesswap +{ + public: + void swap(yesswap&); +}; + +int main() +{ + BOOST_TEST(!boost_move_member_swap::has_member_swap::value); + BOOST_TEST(boost_move_member_swap::has_member_swap::value); + BOOST_TEST(!boost_move_member_swap::has_member_swap::value); + return boost::report_errors(); +} +*/ \ No newline at end of file