"David B. Held" <[EMAIL PROTECTED]> writes:

> I started a new thread because the old one was getting hard to
> follow on my newsreader from the excessive nesting.  I realized
> there is yet another problem with my scheme (which should come
> as no suprise by now).  This time, the problem is the copy c'tor.
> Here's a recap:
>
>     storage::storage(storage const& rhs)
>     : pointee_(rhs.pointee_)
>     { }
>
>     storage::~storage()
>     { checked_delete(pointee_); }
>
>     void storage::release()
>     { pointee_ = default_value(); }
>
>     ref_counted::ref_counted(ref_counted const& rhs)
>     : count_(rhs.count_)
>     { }
>
>     ref_counted::~ref_counted()
>     { delete count_; }
>
>     bool ref_counted::release(P const& p)
>     {
>         if (!--*count_) return true;
>         count_ = 0; return false;
>     }
>
>     smart_ptr::smart_ptr(smart_ptr const& rhs)
>     : storage(rhs), ownership(rhs), checking(rhs), conversion(rhs)
>     { checking::on_init(p); }
>
>     smart_ptr::~smart_ptr()
>     {
>         if (!ownership::release(get_impl(*this)))
>         {
>             storage::release();
>         }
>     }
>
> The problem is that while ref_counted indeed cleans up its count in
> the face of later exceptions, it may do so prematurely.  For instance,
> if checking(rhs) were to throw, ~smart_ptr() does not get a chance
> to call ownership::release(), which prevents ownership from deleting
> the count.  Similarly, if, for some reason, ownership(rhs) were to
> throw, storage might prematurely free the resource.  The two most
> viable solutions I can think of are:
>
> A) add scope guards to make sure the right thing is done no matter
> who throws
>
> B) require that copying be no-throw

  C) Make sure the first base class of smart_ptr is the one that
     manages destruction of its resources:

  template <class Storage, class Ownership>
  struct ptr_manager
     : Storage, Ownership
  {
     ptr_manager(ptr_manager const& p) : Storage(p), Ownership(p) {}

     ~ptr_manager()
     {
         if (!this->Ownership::release(get_impl(*this)))
         {
             this->Storage::release();
         }
      }
  };

   smart_ptr::smart_ptr(smart_ptr const& rhs)
      : ptr_manager(rhs), checking(rhs), conversion(rhs)
   { checking::on_init(p); }


This is just more of the same RAII principles we've been discussing at
work.

I think that whatever orthogonal concepts are being represented by
ownership and storage here (and I'm really not sure I see a clear
separation), they must be sub-concepts of what is normally referred to
as ownership.  You can see that from the fact that you need a
sub-object to group them together to handle resource lifetime
correctly.  I would explore/discuss the implications of not separating
ownership and storage to see whether it's buying you more than it's
costing.

BTW, I don't want to beat this to death, but addressing the issue of
most compilers not doing the EBO for anything other than the first
base class has a huge bearing on all of this, because solving it means
you have to change the base class organization.  For me at least, one
of the primary reasons to use a policy-based smart pointer would be to
get the optimizations it can offer for really time-critical code.  In
fact, at the moment that's the *only* reason I can imagine wanting
something other than boost::shared_ptr<>.  If shared_ptr doesn't have
optimal footprint on most compilers I would be reluctant to use it
where efficiency matters most, and thus it would become useless to me.
I don't know about other people.

-- 
                       David Abrahams
   [EMAIL PROTECTED] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Reply via email to