Boost logo

Boost-Commit :

From: fmhess_at_[hidden]
Date: 2008-03-20 15:32:44


Author: fmhess
Date: 2008-03-20 15:32:43 EDT (Thu, 20 Mar 2008)
New Revision: 43738
URL: http://svn.boost.org/trac/boost/changeset/43738

Log:
Added support for calling enable_shared_from_this::shared_from_this in
constructors. Closes #1696.

Added:
   trunk/libs/smart_ptr/test/esft_constructor_test.cpp (contents, props changed)
Text files modified:
   trunk/boost/enable_shared_from_this.hpp | 51 +++++++++++++++++++----
   trunk/boost/shared_ptr.hpp | 83 +++++++++++++++++++++++++++++++--------
   trunk/libs/smart_ptr/test/Jamfile.v2 | 1
   3 files changed, 108 insertions(+), 27 deletions(-)

Modified: trunk/boost/enable_shared_from_this.hpp
==============================================================================
--- trunk/boost/enable_shared_from_this.hpp (original)
+++ trunk/boost/enable_shared_from_this.hpp 2008-03-20 15:32:43 EDT (Thu, 20 Mar 2008)
@@ -23,9 +23,32 @@
 
 template<class T> 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<T>(dynamic_cast<T *>(const_cast<enable_shared_from_this*>(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 @@
         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<T> shared_from_this()
     {
+ init_internal_shared_once();
         shared_ptr<T> p(_internal_weak_this);
         BOOST_ASSERT(p.get() == this);
         return p;
@@ -53,19 +82,21 @@
 
     shared_ptr<T const> shared_from_this() const
     {
+ init_internal_shared_once();
         shared_ptr<T const> 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<typename U>
+ void _internal_accept_owner(shared_ptr<U> &owner) const
+ {
+ init_internal_shared_once();
+ get_deleter<detail::sp_deleter_wrapper>(_internal_shared_this)->set_deleter(owner);
+ owner = _internal_shared_this;
+ _internal_shared_this.reset();
+ _owned = true;
+ }
 };
 
 } // namespace boost

Modified: trunk/boost/shared_ptr.hpp
==============================================================================
--- trunk/boost/shared_ptr.hpp (original)
+++ trunk/boost/shared_ptr.hpp 2008-03-20 15:32:43 EDT (Thu, 20 Mar 2008)
@@ -48,6 +48,7 @@
 namespace boost
 {
 
+template<class T> class shared_ptr;
 template<class T> class weak_ptr;
 template<class T> class enable_shared_from_this;
 
@@ -90,9 +91,14 @@
 
 // enable_shared_from_this support
 
-template<class T, class Y> void sp_enable_shared_from_this( shared_count const & pn, boost::enable_shared_from_this<T> const * pe, Y const * px )
+struct ignore_enable_shared_from_this_tag {};
+
+template<class T, class Y> void sp_enable_shared_from_this( boost::shared_ptr<Y> * ptr, boost::enable_shared_from_this<T> const * pe )
 {
- if(pe != 0) pe->_internal_weak_this._internal_assign(const_cast<Y*>(px), pn);
+ if(pe != 0)
+ {
+ pe->_internal_accept_owner(*ptr);
+ }
 }
 
 #ifdef _MANAGED
@@ -104,7 +110,7 @@
     template<class T> 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 @@
 # 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< std::auto_ptr< T >, R >
 {
     typedef R type;
-};
+};
 
 #endif
 
@@ -172,7 +178,7 @@
     template<class Y>
     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 @@
 
     template<class Y, class D> 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<class Y, class D, class A> 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 @@
         }
     }
 
+// constructor that doesn't trigger enable_shared_from_this code, needed
+// for enable_shared_from_this internal implementation
+ template<class Y, class D> 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<class Y>
@@ -260,7 +272,7 @@
     {
         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 @@
     {
         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 @@
         BOOST_ASSERT(px != 0);
         return px;
     }
-
+
     T * get() const // never throws
     {
         return px;
@@ -416,13 +428,13 @@
     ( 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 @@
 template<class E, class T, class Y> basic_ostream<E, T> & operator<< (basic_ostream<E, T> & os, shared_ptr<Y> const & p)
 # else
 template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p)
-# endif
+# endif
 {
     os << p.get();
     return os;
@@ -597,6 +609,8 @@
 
 // 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 @@
 // g++ 2.9x doesn't allow static_cast<X const *>(void *)
 // apparently EDG 2.38 and HP aCC A.03.35 also don't accept it
 
-template<class D, class T> D * get_deleter(shared_ptr<T> const & p)
+template<class D, class T> D * basic_get_deleter(shared_ptr<T> const & p)
 {
     void const * q = p._internal_get_deleter(BOOST_SP_TYPEID(D));
     return const_cast<D *>(static_cast<D const *>(q));
@@ -612,18 +626,53 @@
 
 #else
 
-template<class D, class T> D * get_deleter(shared_ptr<T> const & p)
+template<class D, class T> D * basic_get_deleter(shared_ptr<T> const & p)
 {
     return static_cast<D *>(p._internal_get_deleter(BOOST_SP_TYPEID(D)));
 }
 
 #endif
 
+class sp_deleter_wrapper
+{
+ shared_ptr<const void> _deleter;
+public:
+ sp_deleter_wrapper()
+ {}
+ void set_deleter(const shared_ptr<const void> &deleter)
+ {
+ _deleter = deleter;
+ }
+ void operator()(const void *)
+ {
+ BOOST_ASSERT(_deleter.use_count() <= 1);
+ _deleter.reset();
+ }
+ template<typename D>
+ D* get_deleter() const
+ {
+ return boost::detail::basic_get_deleter<D>(_deleter);
+ }
+};
+
+} // namespace detail
+
+template<class D, class T> D * get_deleter(shared_ptr<T> const & p)
+{
+ D *del = detail::basic_get_deleter<D>(p);
+ if(del == 0)
+ {
+ detail::sp_deleter_wrapper *del_wrapper = detail::basic_get_deleter<detail::sp_deleter_wrapper>(p);
+ if(del_wrapper) del = del_wrapper->get_deleter<D>();
+ }
+ return del;
+}
+
 } // namespace boost
 
 #ifdef BOOST_MSVC
 # pragma warning(pop)
-#endif
+#endif
 
 #endif // #if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES)
 

Modified: trunk/libs/smart_ptr/test/Jamfile.v2
==============================================================================
--- trunk/libs/smart_ptr/test/Jamfile.v2 (original)
+++ trunk/libs/smart_ptr/test/Jamfile.v2 2008-03-20 15:32:43 EDT (Thu, 20 Mar 2008)
@@ -36,5 +36,6 @@
           [ compile-fail scoped_ptr_eq_fail.cpp ]
           [ compile-fail scoped_array_eq_fail.cpp ]
           [ run esft_regtest.cpp ]
+ [ run esft_constructor_test.cpp ]
         ;
 }

Added: trunk/libs/smart_ptr/test/esft_constructor_test.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/smart_ptr/test/esft_constructor_test.cpp 2008-03-20 15:32:43 EDT (Thu, 20 Mar 2008)
@@ -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 <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/detail/lightweight_test.hpp>
+#include <memory>
+
+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<X> *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<typename T, typename U>
+bool are_shared_owners(const boost::shared_ptr<T> &a, const boost::shared_ptr<U> &b)
+{
+ return a && !(a < b) && !(b < a);
+}
+
+int main()
+{
+ BOOST_TEST( X::instances == 0 );
+
+ {
+ boost::shared_ptr<X> early_px;
+ X* x = new X( 1, &early_px );
+ BOOST_TEST( early_px.use_count() > 0 );
+ BOOST_TEST( boost::get_deleter<X::deleter_type>(early_px) == 0 );
+ boost::shared_ptr<X> 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<X::deleter_type>(early_px);
+ BOOST_TEST(pd && *pd == &X::deleter2 );
+ }
+
+ BOOST_TEST( X::instances == 0 );
+
+ {
+ boost::shared_ptr<X> early_px;
+ X* x = new X( 1, &early_px );
+ boost::weak_ptr<X> early_weak_px = early_px;
+ early_px.reset();
+ BOOST_TEST( !early_weak_px.expired() );
+ boost::shared_ptr<X> 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<X> early_px;
+ X x( 1, &early_px );
+ BOOST_TEST( early_px.use_count() > 0 );
+ boost::shared_ptr<X> 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<X> early_weak_px;
+ {
+ boost::shared_ptr<X> 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();
+}


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk