Added support for calling enable_shared_from_this::shared_from_this in

constructors.  Closes #1696.



[SVN r43738]
This commit is contained in:
Frank Mori Hess
2008-03-20 19:32:43 +00:00
parent 2eb3991630
commit 1c2d780f9e
4 changed files with 252 additions and 27 deletions

View File

@ -23,9 +23,32 @@ namespace boost
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 @@ 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<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 @@ public:
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

View File

@ -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 @@ template<> struct shared_ptr_traits<void const volatile>
// 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 @@ struct sp_any_pointer
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 @@ 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<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 @@ public:
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 @@ public:
}
}
// 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 @@ 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<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 @@ template<class E, class T, class Y> std::basic_ostream<E, T> & 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<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::
// 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 @@ template<class D, class T> D * get_deleter(shared_ptr<T> const & p)
#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)

View File

@ -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 ]
;
}

View File

@ -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();
}