From 392240a87cca4ae84d0d8c88040b14576e8a7d0a Mon Sep 17 00:00:00 2001 From: mat007 Date: Tue, 13 Aug 2013 21:43:14 +0000 Subject: [PATCH] Added move support in actions git-svn-id: https://svn.code.sf.net/p/turtle/code/trunk@681 860be788-9bd5-4423-9f1e-828f051e677b --- build/boost/doc/changelog.qbk | 1 + build/boost/doc/example/reference.cpp | 1 + build/boost/doc/reference.qbk | 1 + test/detail/test_function.cpp | 51 +++++++++++++++++++ turtle/config.hpp | 6 +++ turtle/detail/action.hpp | 73 ++++++++++++++++++++------- turtle/detail/lambda.hpp | 11 ++++ 7 files changed, 126 insertions(+), 18 deletions(-) diff --git a/build/boost/doc/changelog.qbk b/build/boost/doc/changelog.qbk index 9ba10fa..270fe8a 100644 --- a/build/boost/doc/changelog.qbk +++ b/build/boost/doc/changelog.qbk @@ -7,6 +7,7 @@ Not yet released * Added MOCK_NO_VARIADIC_MACROS to deactivate variadic macros support * Added support for movable only types as parameters * Added logging support for std::unique_ptr, std::shared_ptr and std::weak_ptr +* Added move support in actions [endsect] diff --git a/build/boost/doc/example/reference.cpp b/build/boost/doc/example/reference.cpp index 2cd9462..a4545cd 100644 --- a/build/boost/doc/example/reference.cpp +++ b/build/boost/doc/example/reference.cpp @@ -624,6 +624,7 @@ BOOST_AUTO_TEST_CASE( demonstrates_configuring_actions ) { mock_class c; MOCK_EXPECT( c.method ).returns( 42 ); + MOCK_EXPECT( c.method ).moves( 42 ); // returns by moving the value MOCK_EXPECT( c.method ).throws( std::runtime_error( "error !" ) ); MOCK_EXPECT( c.method ).calls( &function ); // forwards 'method' parameter to 'function' MOCK_EXPECT( c.method ).calls( boost::bind( &function, 42 ) ); // drops 'method' parameter and binds 42 as parameter to 'function' diff --git a/build/boost/doc/reference.qbk b/build/boost/doc/reference.qbk index b082d9d..c8ed416 100644 --- a/build/boost/doc/reference.qbk +++ b/build/boost/doc/reference.qbk @@ -419,6 +419,7 @@ An action performs additional treatments after an expectation has been deemed va Synopsis : MOCK_EXPECT( identifier ).returns( value ); + MOCK_EXPECT( identifier ).moves( value ); MOCK_EXPECT( identifier ).throws( exception ); MOCK_EXPECT( identifier ).calls( functor ); // gets assigned to a boost::function and throws std::invalid_argument if empty diff --git a/test/detail/test_function.cpp b/test/detail/test_function.cpp index b2ca526..ef1eb9c 100644 --- a/test/detail/test_function.cpp +++ b/test/detail/test_function.cpp @@ -555,6 +555,57 @@ BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_returns_the_set_auto_ptr_valu } } +#ifdef MOCK_RVALUE_REFERENCES + +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_moves_the_set_lvalue, error_fixture ) +{ + mock::detail::function< int() > f; + int i = 3; + f.expect().moves( i ); + BOOST_CHECK_NO_THROW( f() ); + CHECK_CALLS( 1 ); +} + +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_moves_the_set_const_lvalue, error_fixture ) +{ + mock::detail::function< int() > f; + const int i = 3; + f.expect().moves( i ); + BOOST_CHECK_NO_THROW( f() ); + CHECK_CALLS( 1 ); +} + +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_moves_the_set_rvalue, error_fixture ) +{ + mock::detail::function< int() > f; + f.expect().moves( 3 ); + BOOST_CHECK_NO_THROW( f() ); + CHECK_CALLS( 1 ); +} + +#endif + +#ifdef MOCK_SMART_PTR + +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_moves_the_set_unique_ptr_lvalue, error_fixture ) +{ + mock::detail::function< std::unique_ptr< int >() > f; + std::unique_ptr< int > p( new int ); + f.expect().moves( std::move( p ) ); + BOOST_CHECK_NO_THROW( f() ); + CHECK_CALLS( 1 ); +} + +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_moves_the_set_unique_ptr_rvalue, error_fixture ) +{ + mock::detail::function< std::unique_ptr< int >() > f; + f.expect().moves( std::unique_ptr< int >( new int ) ); + BOOST_CHECK_NO_THROW( f() ); + CHECK_CALLS( 1 ); +} + +#endif // MOCK_SMART_PTR + BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_returns_the_set_shared_ptr_value, error_fixture ) { mock::detail::function< boost::shared_ptr< A >() > f; diff --git a/turtle/config.hpp b/turtle/config.hpp index a7e4d76..e9dcb1d 100644 --- a/turtle/config.hpp +++ b/turtle/config.hpp @@ -77,4 +77,10 @@ # endif #endif +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_RVALUE_REFERENCES) +# ifndef MOCK_NO_RVALUE_REFERENCES +# define MOCK_RVALUE_REFERENCES +# endif +#endif + #endif // MOCK_CONFIG_HPP_INCLUDED diff --git a/turtle/detail/action.hpp b/turtle/detail/action.hpp index 97cb59d..2f9af52 100644 --- a/turtle/detail/action.hpp +++ b/turtle/detail/action.hpp @@ -13,14 +13,50 @@ #include "lambda.hpp" #include #include +#include +#include +#include #include #include -#include namespace mock { namespace detail { + struct holder : boost::noncopyable + { + virtual ~holder() + {} + }; + template< typename T > + struct holder_imp : holder + { + holder_imp( BOOST_RV_REF( T ) t ) + : t_( boost::move( t ) ) + {} + holder_imp( const T& t ) + : t_( t ) + {} + T t_; + }; + + struct value + { + template< typename T > + T& store( BOOST_RV_REF( T ) t ) + { + h_.reset( new holder_imp< T >( boost::move( t ) ) ); + return static_cast< holder_imp< T >* >( h_.get() )->t_; + } + template< typename T > + T& store( const T& t ) + { + h_.reset( new holder_imp< T >( t ) ); + return static_cast< holder_imp< T >* >( h_.get() )->t_; + } + boost::shared_ptr< holder > h_; + }; + template< typename Result, typename Signature > class action { @@ -35,18 +71,15 @@ namespace detail public: template< typename Value > - void returns( Value v ) + void returns( const Value& v ) { - r_ = v; - f_ = lambda_type::make_val( - boost::ref( boost::any_cast< Value& >( r_ ) ) ); + f_ = lambda_type::make_val( boost::ref( v_.store( v ) ) ); } template< typename Value > void returns( Value* v ) { - r_ = result_type( v ); f_ = lambda_type::make_val( - boost::ref( boost::any_cast< result_type& >( r_ ) ) ); + boost::ref( v_.store( result_type( v ) ) ) ); } template< typename Y > void returns( const boost::reference_wrapper< Y >& r ) @@ -54,6 +87,12 @@ namespace detail f_ = lambda_type::make_val( r ); } + template< typename Value > + void moves( BOOST_RV_REF( Value ) v ) + { + f_ = lambda_type::make_move( v_.store( boost::move( v ) ) ); + } + void calls( const functor_type& f ) { if( ! f ) @@ -73,7 +112,7 @@ namespace detail } private: - boost::any r_; + value v_; functor_type f_; }; @@ -165,9 +204,9 @@ namespace detail action() {} action( const action& rhs ) - : r_( const_cast< action& >( rhs ).r_.release() ) - , f_( r_.get() - ? lambda_type::make_val( boost::ref( r_ ) ) + : v_( const_cast< action& >( rhs ).v_.release() ) + , f_( v_.get() + ? lambda_type::make_val( boost::ref( v_ ) ) : rhs.f_ ) {} @@ -188,7 +227,6 @@ namespace detail void throws( Exception e ) { f_ = lambda_type::make_throw( e ); - r_.reset(); } const functor_type& functor() const @@ -200,23 +238,22 @@ namespace detail template< typename Y > void set( std::auto_ptr< Y > r ) { - r_ = r; - f_ = lambda_type::make_val( boost::ref( r_ ) ); + v_ = r; + f_ = lambda_type::make_val( boost::ref( v_ ) ); } template< typename Y > void set( const boost::reference_wrapper< Y >& r ) { f_ = lambda_type::make_val( r ); - r_.reset(); } template< typename Y > void set( Y* r ) { - r_.reset( r ); - f_ = lambda_type::make_val( boost::ref( r_ ) ); + v_.reset( r ); + f_ = lambda_type::make_val( boost::ref( v_ ) ); } - std::auto_ptr< Result > r_; + std::auto_ptr< Result > v_; functor_type f_; }; } diff --git a/turtle/detail/lambda.hpp b/turtle/detail/lambda.hpp index 0e1ccde..32fc71d 100644 --- a/turtle/detail/lambda.hpp +++ b/turtle/detail/lambda.hpp @@ -15,6 +15,7 @@ #else #include #endif +#include #include namespace mock @@ -45,6 +46,11 @@ namespace detail &do_ref_identity< T >, t.get_pointer() ); } template< typename T > + static functor_type make_move( T& t ) + { + return detail::bind( &do_move< T >, boost::ref( t ) ); + } + template< typename T > static functor_type make_throw( T t ) { return detail::bind( &do_throw< T >, t ); @@ -60,6 +66,11 @@ namespace detail return t; } template< typename T > + static T do_move( T& t ) + { + return boost::move( t ); + } + template< typename T > static T& do_ref_identity( T* t ) { return *t;