Boost logo

Boost :

From: Michel André (michel.andre_at_[hidden])
Date: 1999-11-23 15:15:19


Hi!

> I think the smart pointers are getting too smart for their own good. I
> have a new proposal that pretty much follows from the code Michel André
> posted. The basic idea is to disallow ordinary functions as Deleter's,
> rename Deleter to Allocator, and adopt something very close to the
> std::allocator interface.

> Motivations include:
>
> 1. Cleaner and more efficient code (guaranteed empty member
> optimization).
> 2. Perhaps these animals could be put to good use in implementing the
> std lib.

Sounds good. Lets call this part of an allocator a deallocator wich is
mainly what it is.

Maybe the next step is to generalize the construction of ptrs in smart
pointer objects in which case we need a complete allocor?

>
> Two deleter class's to start off with. They implement just a small
> portion of the std::allocator interface:
>
How about this implementation for clarity?

template<typename T>
class deallocator
{
public:
        typedef std::size_t size_type;
         typedef std::ptrdiff_t difference_type;
         typedef T* pointer;
         typedef const T* const_pointer;
         typedef T& reference;
         typedef const T& const_reference;
         typedef T value_type;

        // functions to implement
         // template <class U> struct rebind { typedef single_deleter<U> other; };
         // void deallocate(pointer p, size_type) {delete p;}
 };

template<typename T>
class single_deleter
{
public:
         template <class U> struct rebind { typedef single_deleter<U> other; };
         void deallocate(pointer p, size_type) {delete p;}
 };

 template<typename T>
 class array_deleter
 {
 public:
         void deallocate(pointer p, size_type) {delete [] p;}
 };

And call all uses of Allocator in the rest of the code
for Deallocator. And state that an deallocator is an subset
of the functionality in an allocator and that an allocator
can be used in place of an deallocator. But that doesn't
work since the rebind declare isn't available in the allocator
interface. So the animal described above isn't an allocator and
can't be used interchangeably. It wouls look like the following
for base_ptr:

template<typename T , typename Deallocator>
class base_ptr
        : noncopyable
{
public:
        typedef Dellocator deallocator_type;
        typedef T element_type;

        T& operator*() const { return *ptr(); }
        T* operator->() const { return ptr(); }
        T* get() const { return ptr(); }

protected:
        base_ptr(T* p = 0, const allocator_type& a = allocator_type()) :
alloc_(a, p) {}

        T*& ptr() { return dealloc_.m; }
        T* ptr() const { return dealloc_.m; }

        allocator_type& deallocator() { return dealloc_; }
        const allocator_type& deallocator() const { return dealloc_; }
private:
        base_opt<Deallocator, T*> dealloc_;
};

You get the idea.

> 2. An extended auto_ptr that can be used with array_deleter:
>
> template<typename X, typename Allocator = single_deleter<X> > class
> auto_ptr;
>
> template <typename Y, typename Allocator = single_deleter<Y> >
> struct auto_ptr_ref
> {
> auto_ptr<Y, Allocator>& p_;
> auto_ptr_ref(const auto_ptr<Y, Allocator>& a) :
> p_(const_cast<auto_ptr<Y, Allocator>&>(a)) {}
> };

This would be a nice feature of auto_ptr that i have lacked. Thats
how i came to use the boost smart ptrs in the first place and beginned
to think about parametrizing the delete or deallocate op. Since i could
think of a numerous of cases when a simple delete wasn't enough or even
couldn't do to deallocate a resource and i didn't want to wrap it up in
a class of its own.

> This is where the rebind comes in handy in the allocators. Note that
> I've messed with the rebind definitions so that single_deleter allows the
> appropriate conversions:
>
> auto_ptr<B> b(new B);
> auto_ptr<B> b2(b);
> b = b2;
> auto_ptr<B> b3(source());
> auto_ptr<A> a(b); // B derives from A
> a = b3;
> b3 = source();
> *b3 = B();
> sink_b(source());
> auto_ptr<A> a2(source()); // Untested
> a2 = source(); // Untested
> sink_a(source()); // Untested
>
> but array_deleter correctly refuses conversions between base and derived:
>
> auto_ptr<B, array_deleter<B> > b(new B [2]);
> auto_ptr<B, array_deleter<B> > b2(b);
> b = b2;
> auto_ptr<B, array_deleter<B> > b3(array_source());
> b3 = array_source();
> array_sink(array_source());
> // auto_ptr<A, array_deleter<A> > a(b3); // Should not compile
> // a = b3; // Should not compile

This looks way cool!

>
> 3. A start on a smart pointer that might be useful in implementing
> vector or deque. Don't have the details worked out yet though:

-- 8< Code snipped --

> This is just a scoped_ptr with a capacity thrown in that it can hand back
> to Allocator::deallocate. Note that scoped_ptr may be useful as is for
> use in node based standard containers because it passes "1" in
> Allocator::deallocate. Again, haven't actually implemented a standard
> container in terms of one of these. Just exploring the possibility.

I really don't see the use of this but maybe you could enlighten me?
Couldnt the standard containers be implemented with an scoped_array or
an auto_ptr with an array deallocator?

Have a nice day
/Michel

PS I will be in London for a couple of days in business so I won't be
online from tomorrow morning (my local time) through friday or something.
So bear with me if there aren't any quick responses ;)


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