Boost logo

Boost :

From: David B. Held (dheld_at_[hidden])
Date: 2003-01-29 21:28:44


"David Abrahams" <dave_at_[hidden]> wrote in message
news:ud6mfcv9c.fsf_at_boost-consulting.com...
> [...]
> It sure does if any of the bases or members of smart_ptr throws
> from its constructor.

Just when I thought the problem was solved...

> [...]
> I dunno. Acquiring ownership at construction time is a key part
> of the "one true meaning" of RAII (not the accepted meaning,
> which has come to be "deallocating resources in destructors" -- a
> concept having nothing to do with acquisition _or_ initialization).
> There's a good reason for this, since it avoids the problem of
> leaking when a constructor throws.

On the other hand, acquiring multiple resources is going to present
exactly the same problem. Or acquiring a resource in any other
context when members can throw will have the same problem. You
could say that no class should acquire a resource and have other
members/bases that could throw, but that seems draconian. So
what is the real solution? I'm beginning to agree with Andrei that
there appears to be a fundamental language problem here, but I
don't pretend to have a change that would fix it. Modifying function
try blocks to be more useful might seem like the answer, but any
change would make them different from normal try blocks, right?

Look at shared_count. It does not acquire the count in the initializer
list. It default-constructs and then acquires the count inside a try
block in the body of the c'tor. Does that mean it does not perform
"True RAII"? I would like to use the initializer list as much as the
next guy, but multiple initializers seem to be a real problem. It works
great for the trivial case. Does this mean "True RAII" doesn't scale?
And this is an RAII problem, not a PBD or orthogonality problem.

That's because as much as we would like to say that every class
should only perform one function, it happens that some functions
cannot be performed with a single resource. For ref-counted smart
pointers (shared_ptr or smart_ptr), those resources are the managed
resource and the count.

A kludge, but perhaps a start in the right direction, would be to have
a special function analogous to the destructor that gets called when
a member is destroyed as the result of a failed construction. It would
be analogous to placement delete. It would only get called on fully
constructed objects. This way, objects would be able to tell when
they are being destroyed as part of their normal life cycle, and when
they are being destroyed due to a problem. One syntax would be to
pass a dummy int parameter to a normal destructor, like so:

scalar_storage::scalar_storage()
{
    // normal destruction, do nothing
}

scalar_storage::~scalar_storage(int)
{
    // something bad happened, clean up with extreme prejudice
    destroy();
}

Another syntax might be to pass the exception type to the destructor.
Then, a given destructor would only get called if its argument were
convertible to the thrown exception type, like so:

scalar_storage::~scalar_storage(std::bad_alloc const&)
{
    // failed allocation somewhere else, clean up
    destroy();
}

scalar_storage::~scalar_storage(std::exception const&)
{
    // some other problem, clean up anyway ;)
    destroy();
}

In the event that no specialized d'tor were defined, the normal
destructor would get called.

So, in this case:

int main()
{
    ref_counted r;
}

At the end of scope, ref_counted::~ref_counted() is called. Whereas,
in this case:

struct X
{
    struct Y { Y() { throw 0; } };

    X() : r(), y() { }

    ref_counted r;
    Y y;
};

ref_counted::~ref_counted(int) would get called.

Unfortunately, shared_count shows that this scheme is not sufficient,
because you lose the context of the throwing c'tor. In particular,
the shared_count(P, D) c'tor is most problematic. On the other
hand, shared_count could technically be written using a function
try block. Who knows. Maybe someone can come up with a
better idea.

Dave


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