Boost logo

Boost :

Subject: Re: [boost] scoped_ptr deleter
From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2009-09-16 11:47:29


I've been lurking on this thread for awhile now, and as a person who
is very familiar with the design and use of single-ownership smart
pointer with custom-deleter capability, I figured it was time to throw
my two cents in...

I find custom deleters handy with single-ownership even when I'm not
trying to move them out of scope. I usually use:

const unique_ptr<MyClass, MyDeleter<A>> p(...);

The const qualifier on the unique_ptr renders the unique_ptr to be
practically the same thing as scoped_ptr. It will take a const_cast
for this animal to transfer the ownership out of the scope.

I've also found that sometimes I'll start with a same-scope use, but
during refactoring, learn that I want to move the smart pointer
creation into a factory function (for reuse in several places). When
I do that, it is handy to not have to change smart pointer types
(besides dropping the const qualifier if I used that).

I've also found support for statefull deleters quite necessary (as
opposed to just handling stateless deleters). The proposed code:

template<class T,
class D = boost::checked_deleter<T> > // first patch line
class scoped_ptr
{
// ...

public:
    ~scoped_ptr()
   {
      // ...
      deleter()(ptr); // second patch line, instead of
boost::checked_delete
   }
};

does not handle statefull deleters. If this community does decide to
put a custom deleter into scoped_ptr (and I am ambivalent on that
decision), then I strongly encourage the designers of this patch to
mimic the custom deleter of unique_ptr. The latest specification of
it can be found here:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2914.pdf

(search for "[unique.ptr]")

And here is a more readable link in tutorial form which hits the
highlights:

http://home.roadrunner.com/~hinnant/unique_ptr03.html

(and includes a C++03 emulation).

The basic layout would look like:

template<class T,
class D = boost::checked_deleter<T> > // checked_deleter<T[]> should
be partially specialized to handle arrays
class scoped_ptr
{
     typedef typename details::compute_pointer<T, D>::type pointer;
     tuple<pointer, D> ptr_;
     ...
};

Until tuple is rewritten with the empty member optimization, you'll
want to use compressed_pair<pointer, D> instead.

The "compute_pointer" business defaults to T* unless D::pointer
exists, in which case pointer is D::pointer. This supports putting
this smart pointer in shared memory. Allocators, which are a common
use case, often have this nested type, and sometimes, it is not a raw
pointer.

Storing the D as shown above will:

1. Optimize away the space for it when D is empty.
2. Support function pointer types.
3. Support non-empty (statefull) Ds.

I also recommend supporting lvalue-reference D types. This is very
handy when dealing with a stateful D that you don't want to move or
copy (say because it is a data member of the enclosing class). Such
support will probably come for free in C++0x, but requires a little
extra work in C++03 to avoid the reference-to-reference situation.

I also recommend deleter accessors which return *references* to the
deleter:

    D& get_deleter();
    const D& get_deleter() const;

It is not uncommon to need access to the actual stored deleter (not a
copy of it) during the lifetime of the smart pointer (I needed this
just yesterday in code I was writing). This is used to change the
state of the deleter which alters the behavior the deallocation
process (e.g. do (or don't) destruct a certain field in the held
object).

And you'll want scoped_ptr constructors which accept a deleter (to
initialize its state). And you'll want to disable the constructors
which don't take a deleter when D is a pointer or reference type (lest
it be null).

scoped_ptr<A[]> could be a nice synonym for scoped_array<A> if
checked_deleter is specialized for T[]. It is nothing more (or less)
than a nice, easy to remember name for the array variant of the smart
pointer.

When you get done, you will have reinvented all of the const parts of
unique_ptr (which has several years of refinement under its belt by
now). I don't necessarily discourage that as it is a very educational
exercise.

-Howard


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