boost::shared_ptr/weak_ptr

I've been maintaining/developing a fairly large application which has been built entirely without the use of exceptions. Two of the major target platforms have weak or no exception support. Anyhow, It appears that the function weak_ptr<T>::lock() relies on an exception thrown by shared_ptr<T>'s constructor in order to work properly. I'm assuming based on the code for lock() that the reason the call uses an exception is because the constructor of shared_ptr uses an atomic operation to perform the reference counting. I was curious if perhaps and appropriate solution for compilers lacking support for exceptions would be to simply make the constructors for boost::shared_ptr construct a shared_ptr equivalent to shared_ptr<T> ptr( 0 ); I'm assuming that if the constructors of shared_ptr never throw (even if the interface indicates so) using weak_ptr::lock() would be safe based on the code below, taken directly from weak_ptr.hpp. shared_ptr<T> lock() const // never throws { #if defined(BOOST_HAS_THREADS) // optimization: avoid throw overhead if(expired()) { return shared_ptr<element_type>(); } try { return shared_ptr<element_type>(*this); } catch(bad_weak_ptr const &) { // Q: how can we get here? // A: another thread may have invalidated r after the use_count test above. return shared_ptr<element_type>(); } #else // optimization: avoid try/catch overhead when single threaded return expired()? shared_ptr<element_type>(): shared_ptr<element_type>(*this); #endif }

Patrick Twohig:
I've been maintaining/developing a fairly large application which has been built entirely without the use of exceptions. Two of the major target platforms have weak or no exception support. Anyhow, It appears that the function weak_ptr<T>::lock() relies on an exception thrown by shared_ptr<T>'s constructor in order to work properly.
Very good point. I consider this a bug; please feel free to add a Trac ticket for it. Unfortunately it's not easy to make a test for that due to the "optimization: avoid throw overhead" early check. :-)

I wrote:
Patrick Twohig:
I've been maintaining/developing a fairly large application which has been built entirely without the use of exceptions. Two of the major target platforms have weak or no exception support. Anyhow, It appears that the function weak_ptr<T>::lock() relies on an exception thrown by shared_ptr<T>'s constructor in order to work properly.
Very good point. I consider this a bug; please feel free to add a Trac ticket for it.
Never mind. I committed a fix: http://svn.boost.org/trac/boost/changeset/44344 Let me know how it works for you.

On Sat, 2008-04-12 at 17:31 +0300, Peter Dimov wrote:
Wouldn't it be easier to make shared_count(weak_count&) to never throw and add if(!pn) boost::throw_exception( boost::bad_weak_ptr() ); to the non-tagged ctor of the shared_ptr? Regards, Daniel

Daniel Frey:
On Sat, 2008-04-12 at 17:31 +0300, Peter Dimov wrote:
Wouldn't it be easier to make shared_count(weak_count&) to never throw and add
if(!pn) boost::throw_exception( boost::bad_weak_ptr() );
to the non-tagged ctor of the shared_ptr?
No. Consider your proposed + template<class Y> + explicit shared_ptr(detail::weak_count const & wc, Y * p): pn(wc) // may throw + { + // it is now safe to copy p, as pn(wc) did not throw + px = p; + } which would change meaning. Also consider that + template<class Y> + shared_ptr(detail::shared_count c, Y * p): px(p) // never throws + { + pn.swap(c); + } is now equivalent to the weak_count constructor because shared_count(weak_count) throws. If it yielded an empty shared_count, you wouldn't be able to tell from inside the body whether you need to throw or not (because passing a genuine empty shared_count is legitimate).

On Sat, 2008-04-12 at 19:01 +0300, Peter Dimov wrote:
Daniel Frey:
On Sat, 2008-04-12 at 17:31 +0300, Peter Dimov wrote:
Wouldn't it be easier to make shared_count(weak_count&) to never throw and add
if(!pn) boost::throw_exception( boost::bad_weak_ptr() );
to the non-tagged ctor of the shared_ptr?
No. Consider your proposed
+ template<class Y> + explicit shared_ptr(detail::weak_count const & wc, Y * p): pn(wc) // may throw + { + // it is now safe to copy p, as pn(wc) did not throw + px = p; + }
which would change meaning.
I'm currently happy with a single additional (and mostly internal) ctor, namely template<class Y> shared_ptr(detail::shared_count & c, Y * p) ... which can be used to solve all cases. Please see the updated patch. One thing you can now do is, let enable_shared_from_this_light throw it's own exception type (e.g. bad_shared_from_this) in case of misuse, as the usage of a weak_ptr or weak_count is just an implementation detail. It also allows to remove the whole sp_nothrow_tag stuff - no need for it anymore... Regards, Daniel
participants (3)
-
Daniel Frey
-
Patrick Twohig
-
Peter Dimov