|
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