Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-01-30 19:53:43


"David B. Held" <dheld_at_[hidden]> writes:

> "David Abrahams" <dave_at_[hidden]> wrote in message
> news:u4r7qe5gb.fsf_at_boost-consulting.com...
>> "David B. Held" <dheld_at_[hidden]> writes:
>> > [...]
>> > smart_ptr(P p)
>> > : storage(p), ownership(p), checking(), conversion()
>> > { checking::on_init(p) }
>> >
>> > 1) storage(p) throws
>>
>> You missed "copying P throws", unless you have prohibited that
>> somewhere. Is P always a pointer type?
>
> Will you cut that out?? You're really beginning to annoy me! ;> Lucky
> for me this time, it was just a case of laziness. The real code is this:
>
> template <typename U>
> smart_ptr(U const& p)
> : base_type(p, detail::init_first_tag())
> { checking_policy::on_init(get_impl(*this)); }
>
> But that's a lot less readable for someone not intimately familiar with
> the implementation. However, I should have specified P const&.

That's OK if the class which ultimately takes posession of p (not
base_type, I think, but storage... or is it ownership?) is _required_
to take it by reference. Does such a requirement exist? If not p
throwing on copy is still an issue to be considered.

>> > [...]
>> > storage(p) must have succeeded, so it is a fully c'ted subobject.
>> > ownership is responsible for cleaning up its own resources, but
>> > nothing else. ~storage() will get called, cleaning up p.
>>
>> Precisely what happens when p is "cleaned up?"
>
> Under the old implementation, "cleaning up p" was equivalent to
> calling storage_policy::destroy(). Now, it's equivalent to calling
> storage_policy::~storage_policy().

That is a very clean approach, and assuming it's OK to keep the
the sole copy of p in storage_policy, even efficient.

>> IIRC the cleanup of p was supposed to be cooperative between
>> storage and ownership.
>
> Actually, ownership just decides *when* to clean up. In this
> particular case, we don't need to consult ownership, because we
> are taking possession of a resource that is assumed to be
> unmanaged. Thus, we are free to clean it up with extreme
> prejudice.

OK

>> At this point no ownership object is available. Is "cleanup" always
>> the same sequence of operations, using a static member of
>> ownership to avoid needing object state?
>
> Ownership may have state, but but it need only be consulted when
> the resource could possibly be shared or moved. That's only
> possible if the smart_ptr c'tor has completed.

I'm not sure that's true for intrusively-counted types.

> If the resource is not yet shared or moved, then we own it
> exclusively, and can destroy it at will.
>
>> [...]
>> > Let's fix ref_counted:
>> >
>> > ~ref_counted()
>> > {
>> > delete pCount_;
>> > }
>> >
>> > bool release(P const&)
>> > {
>> > if (!--*pCount_) return true;
>> > pCount_ = 0;
>> > return false;
>> > }
>> >
>> > Ok, now ownership will properly clean up after itself.
>>
>> That is not completely clear to me based on what you've said
>> so far, because I'm not looking at enough of the code (and I
>> don't really want to ;->)
>
> Then take my word for it. ;) It's the same logic as for storage(p),
> and you blessed that. Essentially, we've applied RAII again (and
> note that it has *everything* to do with the d'tor, despite what you
> said before ;).

I didn't mean that the dtor was unimportant to true RAII. It's
obviously crucial, but everyone always focuses on that and it's so
obvious. Yet the name RAII clearly points towards something else!
Think of it as a mental exercise: "keep your eye on the constructors."

>> [...]
>> You can work through the implications yourself:
>>
>> int main()
>> {
>> smart_ptr<int> x;
>> ...
>> }
>>
>> When x is destroyed, by looking at the definition of all these
>> inline/templated functions, can _you_ deduce that ownership
>> nulls the pointer, leaving nothing for storage to do?
>
> No, I can't. But maybe that's because I'm looking at the full
> interface, which includes things like x = y and reset(x, p).

I think I just misunderstood what the different release() functions
were doing. I thought either one could actually end up deleting the
pointer, but that what I called "the fallback" was just there in case
of some incomplete construction state due to an exception. Of course,
one of the nice thing about EH in C++ is that we never get
incompletely constructed objects, a fact I thought you were
overlooking. The full interface would've been irrelevant in that
case.

But now I understand that "it ain't that", so nevermind.

-Dave

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

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk