From d34d63899896b66f50c46bbfd3a892a141eae102 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sun, 22 Mar 2009 21:11:17 +0000 Subject: [PATCH] Bring back the constructor-enabled enable_shared_from_this as enable_shared_from_this2. [SVN r51912] --- .../smart_ptr/enable_shared_from_this2.hpp | 142 ++++++++------- include/boost/smart_ptr/shared_ptr.hpp | 9 + include/boost/smart_ptr/weak_ptr.hpp | 5 + test/Jamfile.v2 | 1 + test/esft_constructor_test.cpp | 169 ++++++++++++++++++ 5 files changed, 254 insertions(+), 72 deletions(-) create mode 100644 test/esft_constructor_test.cpp diff --git a/include/boost/smart_ptr/enable_shared_from_this2.hpp b/include/boost/smart_ptr/enable_shared_from_this2.hpp index b624ee9..a5bfcff 100644 --- a/include/boost/smart_ptr/enable_shared_from_this2.hpp +++ b/include/boost/smart_ptr/enable_shared_from_this2.hpp @@ -1,16 +1,15 @@ -#ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED -#define BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED +#ifndef BOOST_ENABLE_SHARED_FROM_THIS2_HPP_INCLUDED +#define BOOST_ENABLE_SHARED_FROM_THIS2_HPP_INCLUDED // -// enable_shared_from_this.hpp +// enable_shared_from_this2.hpp // -// Copyright (c) 2002 Peter Dimov +// Copyright 2002, 2009 Peter Dimov +// Copyright 2008 Frank Mori Hess // -// 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) -// -// http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html +// 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 @@ -21,114 +20,113 @@ namespace boost { -#if !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) +namespace detail +{ -template< class T > class enable_shared_from_this; -template< class T, class Y > void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe ); -template< class T, class Y > void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe, void * /*pd*/ ); +class esft2_deleter_wrapper +{ +private: -#endif + shared_ptr deleter_; -template< class T > class enable_shared_from_this +public: + + esft2_deleter_wrapper() + { + } + + template< class T > void set_deleter( shared_ptr const & deleter ) + { + deleter_ = deleter; + } + + template< class T> void operator()( T* ) + { + BOOST_ASSERT( deleter_.use_count() <= 1 ); + deleter_.reset(); + } +}; + +} // namespace detail + +template< class T > class enable_shared_from_this2 { protected: - enable_shared_from_this() + enable_shared_from_this2() { } - enable_shared_from_this(enable_shared_from_this const &) + enable_shared_from_this2( enable_shared_from_this2 const & ) { } - enable_shared_from_this & operator=(enable_shared_from_this const &) + enable_shared_from_this2 & operator=( enable_shared_from_this2 const & ) { return *this; } -// virtual destructor because we need a vtable for dynamic_cast from base to derived to work - virtual ~enable_shared_from_this() + ~enable_shared_from_this2() { - BOOST_ASSERT( _shared_count.use_count() <= 1 ); // make sure no dangling shared_ptr objects exist + BOOST_ASSERT( shared_this_.use_count() <= 1 ); // make sure no dangling shared_ptr objects exist } +private: + + mutable weak_ptr weak_this_; + mutable shared_ptr shared_this_; + public: shared_ptr shared_from_this() { init_weak_once(); - T * p = dynamic_cast( this ); - return shared_ptr( detail::shared_count( _weak_count ), p ); + return shared_ptr( weak_this_ ); } shared_ptr shared_from_this() const { init_weak_once(); - T const * p = dynamic_cast( this ); - return shared_ptr( detail::shared_count( _weak_count ), p ); + return shared_ptr( weak_this_ ); } private: - mutable detail::weak_count _weak_count; - mutable detail::shared_count _shared_count; - void init_weak_once() const { - if( _weak_count.empty() ) + if( weak_this_._empty() ) { - detail::shared_count( (void*)0, detail::sp_deleter_wrapper() ).swap( _shared_count ); - _weak_count = _shared_count; + shared_this_.reset( static_cast< T* >( 0 ), detail::esft2_deleter_wrapper() ); + weak_this_ = shared_this_; } } -#if !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) +public: // actually private, but avoids compiler template friendship issues - template< class U, class Y > friend void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe ); - template< class U, class Y > friend void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe, void * /*pd*/ ); - -#else - -public: - -#endif - - template - void sp_accept_owner( shared_ptr & owner ) const + // Note: invoked automatically by shared_ptr; do not call + template void _internal_accept_owner( shared_ptr * ppx, Y * py ) const { - if( _weak_count.use_count() == 0 ) - { - _weak_count = owner.get_shared_count(); - } - else if( !_shared_count.empty() ) - { - BOOST_ASSERT( owner.unique() ); // no weak_ptrs to owner should exist either, but there's no way to check that - detail::sp_deleter_wrapper * pd = detail::basic_get_deleter( _shared_count ); - BOOST_ASSERT( pd != 0 ); - pd->set_deleter( owner.get_shared_count() ); + BOOST_ASSERT( ppx != 0 ); - owner.reset( _shared_count, owner.get() ); - detail::shared_count().swap( _shared_count ); + if( weak_this_.use_count() == 0 ) + { + weak_this_ = shared_ptr( *ppx, py ); + } + else if( shared_this_.use_count() != 0 ) + { + BOOST_ASSERT( ppx->unique() ); // no weak_ptrs should exist either, but there's no way to check that + + detail::esft2_deleter_wrapper * pd = boost::get_deleter( shared_this_ ); + BOOST_ASSERT( pd != 0 ); + + pd->set_deleter( *ppx ); + + ppx->reset( shared_this_, ppx->get() ); + shared_this_.reset(); } } }; -template< class T, class Y > inline void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe ) -{ - if( pe != 0 ) - { - pe->sp_accept_owner( *ptr ); - } -} - -template< class T, class Y > inline void sp_accept_owner( shared_ptr * ptr, enable_shared_from_this const * pe, void * /*pd*/ ) -{ - if( pe != 0 ) - { - pe->sp_accept_owner( *ptr ); - } -} - } // namespace boost -#endif // #ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED +#endif // #ifndef BOOST_ENABLE_SHARED_FROM_THIS2_HPP_INCLUDED diff --git a/include/boost/smart_ptr/shared_ptr.hpp b/include/boost/smart_ptr/shared_ptr.hpp index 0ce3b97..1cddc04 100644 --- a/include/boost/smart_ptr/shared_ptr.hpp +++ b/include/boost/smart_ptr/shared_ptr.hpp @@ -61,6 +61,7 @@ namespace boost template class shared_ptr; template class weak_ptr; template class enable_shared_from_this; +template class enable_shared_from_this2; namespace detail { @@ -109,6 +110,14 @@ template< class X, class Y, class T > inline void sp_enable_shared_from_this( bo } } +template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr * ppx, Y const * py, boost::enable_shared_from_this2< T > const * pe ) +{ + if( pe != 0 ) + { + pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); + } +} + #ifdef _MANAGED // Avoid C4793, ... causes native code generation diff --git a/include/boost/smart_ptr/weak_ptr.hpp b/include/boost/smart_ptr/weak_ptr.hpp index bf5296a..d21b874 100644 --- a/include/boost/smart_ptr/weak_ptr.hpp +++ b/include/boost/smart_ptr/weak_ptr.hpp @@ -124,6 +124,11 @@ public: return pn.use_count() == 0; } + bool _empty() const // extension, not in std::weak_ptr + { + return pn.empty(); + } + void reset() // never throws in 1.30+ { this_type().swap(*this); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c862197..eacee9a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -56,5 +56,6 @@ import testing ; [ run sp_recursive_assign2_test.cpp ] [ run sp_recursive_assign_rv_test.cpp ] [ run sp_recursive_assign2_rv_test.cpp ] + [ run esft_constructor_test.cpp ] ; } diff --git a/test/esft_constructor_test.cpp b/test/esft_constructor_test.cpp new file mode 100644 index 0000000..ced24e2 --- /dev/null +++ b/test/esft_constructor_test.cpp @@ -0,0 +1,169 @@ +// +// esft_constructor_test.cpp +// +// A test for the new enable_shared_from_this support for calling +// shared_from_this from constructors (that is, prior to the +// object's ownership being passed to an external shared_ptr). +// +// Copyright (c) 2008 Frank Mori Hess +// Copyright (c) 2008 Peter Dimov +// +// 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 +#include +#include +#include +#include + +class X: public boost::enable_shared_from_this2< X > +{ +private: + + int destroyed_; + int deleted_; + int expected_; + +private: + + X( X const& ); + X& operator=( X const& ); + +public: + + static int instances; + +public: + + explicit X( int expected, boost::shared_ptr *early_px = 0 ): destroyed_( 0 ), deleted_( 0 ), expected_( expected ) + { + ++instances; + if( early_px ) *early_px = shared_from_this(); + } + + ~X() + { + BOOST_TEST( deleted_ == expected_ ); + BOOST_TEST( destroyed_ == 0 ); + ++destroyed_; + --instances; + } + + typedef void (*deleter_type)( X* ); + + static void deleter( X * px ) + { + ++px->deleted_; + } + + static void deleter2( X * px ) + { + ++px->deleted_; + delete px; + } +}; + +int X::instances = 0; + +template +bool are_shared_owners(const boost::shared_ptr &a, const boost::shared_ptr &b) +{ + return !(a < b) && !(b < a); +} + +struct Y: public boost::enable_shared_from_this2 +{}; + +int main() +{ + BOOST_TEST( X::instances == 0 ); + + { + boost::shared_ptr early_px; + X* x = new X( 1, &early_px ); + BOOST_TEST( early_px.use_count() > 0 ); + BOOST_TEST( boost::get_deleter(early_px) == 0 ); + boost::shared_ptr px( x, &X::deleter2 ); + BOOST_TEST( early_px.use_count() == 2 && px.use_count() == 2 ); + BOOST_TEST(are_shared_owners(early_px, px)); + px.reset(); + BOOST_TEST( early_px.use_count() == 1 ); + BOOST_TEST( X::instances == 1 ); + // X::deleter_type *pd = boost::get_deleter(early_px); + // BOOST_TEST(pd && *pd == &X::deleter2 ); + } + + BOOST_TEST( X::instances == 0 ); + + { + boost::shared_ptr early_px; + X* x = new X( 1, &early_px ); + boost::weak_ptr early_weak_px = early_px; + early_px.reset(); + BOOST_TEST( !early_weak_px.expired() ); + boost::shared_ptr px( x, &X::deleter2 ); + BOOST_TEST( px.use_count() == 1 ); + BOOST_TEST( X::instances == 1 ); + BOOST_TEST(are_shared_owners(early_weak_px.lock(), px)); + px.reset(); + BOOST_TEST( early_weak_px.expired() ); + } + + BOOST_TEST( X::instances == 0 ); + + { + boost::shared_ptr early_px; + X x( 1, &early_px ); + BOOST_TEST( early_px.use_count() > 0 ); + boost::shared_ptr px( &x, &X::deleter ); + BOOST_TEST( early_px.use_count() == 2 && px.use_count() == 2 ); + early_px.reset(); + BOOST_TEST( px.use_count() == 1 ); + BOOST_TEST( X::instances == 1 ); + px.reset(); + try + { + x.shared_from_this(); + BOOST_ERROR("x did not throw bad_weak_ptr"); + } + catch( const boost::bad_weak_ptr & ) + {} + } + + BOOST_TEST( X::instances == 0 ); + + { + boost::weak_ptr early_weak_px; + { + boost::shared_ptr early_px; + X x( 0, &early_px ); + early_weak_px = early_px; + early_px.reset(); + BOOST_TEST( !early_weak_px.expired() ); + BOOST_TEST( X::instances == 1 ); + } + BOOST_TEST( early_weak_px.expired() ); + } + + BOOST_TEST( X::instances == 0 ); + + { + boost::shared_ptr px(new Y()); + Y y(*px); + px.reset(); + try + { + y.shared_from_this(); + } + catch( const boost::bad_weak_ptr & ) + { + BOOST_ERROR("y threw bad_weak_ptr"); + } + } + + return boost::report_errors(); +}