diff --git a/include/boost/enable_shared_from_this.hpp b/include/boost/enable_shared_from_this.hpp index 4e49f1f..06c8bc7 100644 --- a/include/boost/enable_shared_from_this.hpp +++ b/include/boost/enable_shared_from_this.hpp @@ -23,9 +23,32 @@ namespace boost template class enable_shared_from_this { +// dynamic cast to template type doesn't work in constructor, so we have +// to use lazy initialization + void init_internal_shared_once() const + { + if(owned() == false && _internal_shared_this == 0) + { + _internal_shared_this = shared_ptr(dynamic_cast(const_cast(this)), + detail::sp_deleter_wrapper(), detail::ignore_enable_shared_from_this_tag()); + BOOST_ASSERT(_internal_shared_this.get() == this); + _internal_weak_this = _internal_shared_this; + } + } + + bool owned() const + { + return _owned; + } + + typedef T _internal_element_type; // for bcc 5.5.1 + mutable shared_ptr<_internal_element_type> _internal_shared_this; + mutable weak_ptr<_internal_element_type> _internal_weak_this; + mutable bool _owned; protected: - enable_shared_from_this() + enable_shared_from_this(): + _owned(false) { } @@ -38,14 +61,20 @@ protected: return *this; } - ~enable_shared_from_this() +// virtual destructor because we need a vtable for dynamic_cast from base to derived to work + virtual ~enable_shared_from_this() { +// make sure no dangling shared_ptr objects were created by the +// user calling shared_from_this() but never passing ownership of the object +// to a shared_ptr. + BOOST_ASSERT(owned() || _internal_shared_this.use_count() <= 1); } public: shared_ptr shared_from_this() { + init_internal_shared_once(); shared_ptr p(_internal_weak_this); BOOST_ASSERT(p.get() == this); return p; @@ -53,19 +82,21 @@ public: shared_ptr shared_from_this() const { + init_internal_shared_once(); shared_ptr p(_internal_weak_this); BOOST_ASSERT(p.get() == this); return p; } -// Note: No, you don't need to initialize _internal_weak_this -// -// Please read the documentation, not the code -// -// http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html - - typedef T _internal_element_type; // for bcc 5.5.1 - mutable weak_ptr<_internal_element_type> _internal_weak_this; + template + void _internal_accept_owner(shared_ptr &owner) const + { + init_internal_shared_once(); + get_deleter(_internal_shared_this)->set_deleter(owner); + owner = _internal_shared_this; + _internal_shared_this.reset(); + _owned = true; + } }; } // namespace boost diff --git a/include/boost/shared_ptr.hpp b/include/boost/shared_ptr.hpp index fa08d35..5ab6452 100644 --- a/include/boost/shared_ptr.hpp +++ b/include/boost/shared_ptr.hpp @@ -48,6 +48,7 @@ namespace boost { +template class shared_ptr; template class weak_ptr; template class enable_shared_from_this; @@ -90,9 +91,14 @@ template<> struct shared_ptr_traits // enable_shared_from_this support -template void sp_enable_shared_from_this( shared_count const & pn, boost::enable_shared_from_this const * pe, Y const * px ) +struct ignore_enable_shared_from_this_tag {}; + +template void sp_enable_shared_from_this( boost::shared_ptr * ptr, boost::enable_shared_from_this const * pe ) { - if(pe != 0) pe->_internal_weak_this._internal_assign(const_cast(px), pn); + if(pe != 0) + { + pe->_internal_accept_owner(*ptr); + } } #ifdef _MANAGED @@ -104,7 +110,7 @@ struct sp_any_pointer template sp_any_pointer( T* ) {} }; -inline void sp_enable_shared_from_this( shared_count const & /*pn*/, sp_any_pointer, sp_any_pointer ) +inline void sp_enable_shared_from_this( sp_any_pointer, sp_any_pointer ) { } @@ -115,7 +121,7 @@ inline void sp_enable_shared_from_this( shared_count const & /*pn*/, sp_any_poin # pragma set woff 3506 #endif -inline void sp_enable_shared_from_this( shared_count const & /*pn*/, ... ) +inline void sp_enable_shared_from_this( ... ) { } @@ -136,7 +142,7 @@ template< class T, class R > struct sp_enable_if_auto_ptr template< class T, class R > struct sp_enable_if_auto_ptr< std::auto_ptr< T >, R > { typedef R type; -}; +}; #endif @@ -172,7 +178,7 @@ public: template explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete { - boost::detail::sp_enable_shared_from_this( pn, p, p ); + boost::detail::sp_enable_shared_from_this( this, p ); } // @@ -183,14 +189,14 @@ public: template shared_ptr(Y * p, D d): px(p), pn(p, d) { - boost::detail::sp_enable_shared_from_this( pn, p, p ); + boost::detail::sp_enable_shared_from_this( this, p ); } // As above, but with allocator. A's copy constructor shall not throw. template shared_ptr( Y * p, D d, A a ): px( p ), pn( p, d, a ) { - boost::detail::sp_enable_shared_from_this( pn, p, p ); + boost::detail::sp_enable_shared_from_this( this, p ); } // generated copy constructor, assignment, destructor are fine... @@ -253,6 +259,12 @@ public: } } +// constructor that doesn't trigger enable_shared_from_this code, needed +// for enable_shared_from_this internal implementation + template shared_ptr(Y * p, D d, detail::ignore_enable_shared_from_this_tag tag): + px(p), pn(p, d) + {} + #ifndef BOOST_NO_AUTO_PTR template @@ -260,7 +272,7 @@ public: { Y * tmp = r.get(); pn = boost::detail::shared_count(r); - boost::detail::sp_enable_shared_from_this( pn, tmp, tmp ); + boost::detail::sp_enable_shared_from_this( this, tmp ); } #if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) @@ -270,7 +282,7 @@ public: { typename Ap::element_type * tmp = r.get(); pn = boost::detail::shared_count( r ); - boost::detail::sp_enable_shared_from_this( pn, tmp, tmp ); + boost::detail::sp_enable_shared_from_this( this, tmp ); } @@ -382,7 +394,7 @@ public: BOOST_ASSERT(px != 0); return px; } - + T * get() const // never throws { return px; @@ -416,13 +428,13 @@ public: ( defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590) ) typedef T * (this_type::*unspecified_bool_type)() const; - + operator unspecified_bool_type() const // never throws { return px == 0? 0: &this_type::get; } -#else +#else typedef T * this_type::*unspecified_bool_type; @@ -583,7 +595,7 @@ using std::basic_ostream; template basic_ostream & operator<< (basic_ostream & os, shared_ptr const & p) # else template std::basic_ostream & operator<< (std::basic_ostream & os, shared_ptr const & p) -# endif +# endif { os << p.get(); return os; @@ -597,6 +609,8 @@ template std::basic_ostream & operator<< (std:: // get_deleter +namespace detail +{ #if ( defined(__GNUC__) && BOOST_WORKAROUND(__GNUC__, < 3) ) || \ ( defined(__EDG_VERSION__) && BOOST_WORKAROUND(__EDG_VERSION__, <= 238) ) || \ ( defined(__HP_aCC) && BOOST_WORKAROUND(__HP_aCC, <= 33500) ) @@ -604,7 +618,7 @@ template std::basic_ostream & operator<< (std:: // g++ 2.9x doesn't allow static_cast(void *) // apparently EDG 2.38 and HP aCC A.03.35 also don't accept it -template D * get_deleter(shared_ptr const & p) +template D * basic_get_deleter(shared_ptr const & p) { void const * q = p._internal_get_deleter(BOOST_SP_TYPEID(D)); return const_cast(static_cast(q)); @@ -612,18 +626,53 @@ template D * get_deleter(shared_ptr const & p) #else -template D * get_deleter(shared_ptr const & p) +template D * basic_get_deleter(shared_ptr const & p) { return static_cast(p._internal_get_deleter(BOOST_SP_TYPEID(D))); } #endif +class sp_deleter_wrapper +{ + shared_ptr _deleter; +public: + sp_deleter_wrapper() + {} + void set_deleter(const shared_ptr &deleter) + { + _deleter = deleter; + } + void operator()(const void *) + { + BOOST_ASSERT(_deleter.use_count() <= 1); + _deleter.reset(); + } + template + D* get_deleter() const + { + return boost::detail::basic_get_deleter(_deleter); + } +}; + +} // namespace detail + +template D * get_deleter(shared_ptr const & p) +{ + D *del = detail::basic_get_deleter(p); + if(del == 0) + { + detail::sp_deleter_wrapper *del_wrapper = detail::basic_get_deleter(p); + if(del_wrapper) del = del_wrapper->get_deleter(); + } + return del; +} + } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) -#endif +#endif #endif // #if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index cd51539..837c83c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -36,5 +36,6 @@ import testing ; [ compile-fail scoped_ptr_eq_fail.cpp ] [ compile-fail scoped_array_eq_fail.cpp ] [ run esft_regtest.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..88fbe68 --- /dev/null +++ b/test/esft_constructor_test.cpp @@ -0,0 +1,144 @@ +// +// 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_this< 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 && !(a < b) && !(b < a); +} + +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 ); + } + + 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 ); + + return boost::report_errors(); +}