Boost logo

Boost :

From: Andrei Alexandrescu (andrewalex_at_[hidden])
Date: 2002-05-02 03:45:43


"Phil Nash" <phil.nash.lists_at_[hidden]> wrote in message
news:000001c1f1ab$32e848f0$700a0c0a_at_TimeMachine...
> In which case, FWIW, I would add my voice of agreement that
something like
> shared_ptr could be the "standard" way to pass a shared pointer
across a
> published interface,

I will try to convince you - and everybody - otherwise.

> Andrei, put in that light does what am saying make more sense and
sound
> reasonable now (I think it means I was agreeing with you more than I
thought
> right from the start)?

I don't have much time right now, and I plan to write a lenghty post
on the subject "Binary compatibility of smart pointers: a red
herring?" later - if ever. I am behind my sleep, and I have tons of
homeworks and projects to worry about.

I'll give some quick facts now.

1. Shared pointers allow you to change at runtime the function with
which the pointer is destroyed.

The main reason, it's been said, is to allow library writers to change
that function without having to change the binary layout of the code.

This is indeed the case with dynamically-linked libraries. However, if
the library is statically linked, the following construct offers the
same capabilities, without any overhead:

// Before
shared_ptr<FILE> sp(pFile, fclose);
// After
shared_ptr<FILE, &fclose> sp(pFile);

This would allow the implementer of FILE and fclose to change the
implementation of fclose from a release to the next, and have the
client code continue to work after a relink. It might be the case, but
NOT often, that a programmer would hook into an already-existing
shared_ptr<FILE> /at runtime/ and would change its destroyer with
another function. I'm sure that that might be the case sometimes; but
I argue that this is seldom the case.

So the situation that's helped by keeping the function pointer in the
body of shared_ptr is dynamic linking or changing the function that
destroys a pointer at runtime. For helping this situation, all code
must pay. I argue that the case is not common enough to warrant
imposing such an overhead to all libraries that want to share
pointers.

2. shared_ptr has significant size and speed overhead for COM and
CORBA-style smart pointers

In COM, a smart pointer is as large as a regular pointer. Copying and
destroying the pointer is as expensive as a virtual function call. COM
objects usually do not inherit boost shared_base, and many times users
don't have access to the source code to make this change.

Adding an extra level of indirection when deallocating pointers
roughly doubles the cost of destroying. Also, there is size overhead
(I forgot how much).

Refcounting COM and CORBA pointers is optimized already for proxy
situation, so there's no reason to "reoptimize" it via another
indirect call. That's a pessimization.

I know that these are reasons enough for people I used to work with -
and I believe for most COM (and CORBA) programmers - to stay away from
shared_ptr for any COM work and use Microsoft's CComPtr<T> class which
is designed for the exact task of managing COM pointers.

3. I believe that having a "default smart pointer type" is not such a
good idea.

(To generalize a bit, when I hear of this notion of "default type" I
become wary. The Standard, for example, recommends vector as the
"default container type". What do you mean, "default"? "I don't know
what type to choose for this variable, so let it be float". What
gives? Oh well.)

If a library has to pass around COM pointers and pointers to non-COM
objects, in my humble opinion, it's not a good idea to use the same
smart pointer type for both. It's just a obstination to preserve a
situation that brings no advantage to anyone. Give to the Caesar...
you know. Use COM-tuned pointers for COM objects; use smart pointers
tuned for your objects, for your objects. Make them syntactically
uniform, and make them perform well for the objects they manage.
Provide whatever features you believe are useful. Do you want ultimate
runtime polymorphysm? Go for a slower, runtime-flexible interface like
shared_ptr has. Do you just want a straight external refcounting
interface? Go for it. Do you have a faiblesse for reference linked
pointers? That's cool, too. Make your choices, define the interfaces
you need, and that's that.

4. shared_ptr is a point in smart_ptr's design space.

I might start sounding like a broker record here, but shared_ptr's
behavior is /one/ fixed design in a rich design space offered by a
smart pointer based on policies. I can't understand how a design with
a hardcoded set of decisions embedded inside of it can do better and
more than a framework that grants you that specific design all right,
in addition to many other useful designs.

5. Syntactic convenience

This is, in my humble opinion, the last bastion of hardcoded smart
pointer designs. As I showed in my post "Proposal for easying
smart_ptr usage within C++98", syntactic convenience can be brought
within acceptable bounds (in my opinion) with inner typedefs: replace
'shared_ptr<T>' with 'ptr<T>::shared'.

I'd love to write more on the subject, but look at the time...

Andrei the sleep-deprived one


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