$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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();
+}