Boost logo

Boost :

From: Michel André (michel.andre_at_[hidden])
Date: 2000-03-09 09:33:36


Hello!

I was a part of the discussion and is currently using my
own definitions of the smart_ptr classes which included
the empty base member optimization and parmetrize the delete/
release operation. It's based on the discussion Greg, Beman,
Howard, Me and several others had late last year. I use it
for general purposes to release Windows NT handles and such.

> > o I wonder, whether it would be useful to parametrize the release
operation,
> > at least for scoped_ptr and scoped_array.
> > Couldn't we implement a template for two types, the element type
and the
> > release operation. This would allow to use the "resource
acquisition is
> > initialization" idiom with other acquisitions than new and new[].
>
> Yes, we have discussed this before and it is on my to-do list.
> Unfortunately I am very busy with my main job these days and
> am not making much progress on that list.
>
> > Attached is an example implementation.

I enclose a copy of my definitions wich I use in one of my projects
currently. It also has an implementetation of the so much discussed
release operation (which fits my purposes but may not fit in a broader
implementation, because its very Q&D).

Regards
/Michel

//*********************************************************************
********//
// Michel André - Exmentis Software
// E-mail: michel.andre_at_[hidden]
//
// (C) Copyright Exmentis Software 2000. Permission to use, modify
this
// software is granted provided this copyright notice appears in all
copies.
// This software is provided "as is" without express or implied
warranty, and
// with no claim as to its suitability for any purpose.
//
// xstl.h Standard template library enhancement
// classes/routines/algorithms
//
// Part (smart pointer family of classes) of the code is based
// on code from www.boost.org.
// It's extended and changed to fit my purposes.
//
// Copyright notice from boost:
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. Permission to
copy,
// use, modify, sell and distribute this software is granted provided
this
// copyright notice appears in all copies. This software is provided
"as is"
// without express or implied warranty, and with no claim as to its
// suitability for any purpose.
//*********************************************************************
******//

// class noncopyable -------------------------------------------------
-------//
        // Private copy constructor and copy assignment ensure classes
        // derived from
        // class noncopyable cannot be copied.

        class noncopyable
        {
        protected:
                noncopyable() {}

        private: // emphasize the following members are private
                noncopyable( const noncopyable& );
                const noncopyable& operator=( const noncopyable& );
        }; // noncopyable

        template<typename T> class deleter_function : public
std::unary_function<T*&,void>
        {
        public:
        };

        template<typename T> class deleter : public deleter_function<T>
        {
        public:
                void operator()(T*& p) throw() { delete p; p = NULL; };
        };

        template<typename T> class array_deleter : public deleter_function<T>
        {
        public:
                void operator()(T*& p) throw() { delete[] p; p = NULL; };
        };

        // Class that does member base optimization over the
        // deleter class. To piggyback an empty class instance
        // on an existing member to minimize class size
        template <typename Base, typename Member> struct base_opt : Base {
                Member m;
                base_opt(Base const& b, Member const& m) : Base(b), m(m) {}
                base_opt(const base_opt& rhs) throw() : Base(rhs) { m = rhs.m; }
        };

        template<typename T , typename Deleter> class base_ptr : noncopyable
        {
        public:
                typedef T element_type;

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

        protected:
                T*& ptr() { return deleter.m; }
                T* ptr() const { return deleter.m; }
                void del() { deleter(deleter.m); }
                void del(T* p) { deleter(p); };
                base_ptr(T* p,Deleter d) throw() : deleter(d,p) {};
                base_ptr(const base_ptr& rhs) throw() : deleter(rhs.deleter) { }

        private:
                
                base_opt< Deleter, T* > deleter;
        };

        // scoped_ptr ------------------------------------------------------
--------//
        // scoped_ptr mimics a built-in pointer except that it guarantees
deletion
        // of the object pointed to, either on destruction of the scoped_ptr
or via
        // an explicit reset(). scoped_ptr is a simple solution for simple
needs;
        // see shared_ptr (below) or std::auto_ptr if your needs are more
complex.
        template< typename T , typename Deleter = deleter<T> > class
scoped_ptr : public base_ptr< T , Deleter >
        {
        public:
                typedef T element_type;

                explicit scoped_ptr( T* p=0, const Deleter& d = deleter<T>()) throw()
: base_ptr<T,Deleter>(p,d) {}
                ~scoped_ptr() { reset(); }

                void reset( T* p=0 )
                {
                        if ( ptr() != p )
                        {
                                del();
                                ptr() = p;
                        }
                }
        }; // scoped_ptr

        // scoped_array ----------------------------------------------------
--------//
        // scoped_array extends scoped_ptr to arrays. Deletion of the array
pointed to
        // is guaranteed, either on destruction of the scoped_array or via an
explicit
        // reset(). See shared_array or std::vector if your needs are more
complex.
        template< typename T , typename Deleter = array_deleter<T> > class
scoped_array : public scoped_ptr<T,Deleter>
        {
        public:
                typedef T element_type;
                explicit scoped_array( T* p=0,const Deleter& d = array_deleter<T>())
throw() : scoped_ptr<T,Deleter>(p,d) {}
                ~scoped_array() { reset(); }
                T& operator[](size_t i) const throw() { return ptr()[i]; }
        }; // scoped_array

        // shared_ptr ------------------------------------------------------
--------//
        // An enhanced relative of scoped_ptr with reference counted copy
semantics.
        // The object pointed to is deleted when the last shared_ptr pointing
to it
        // is destroyed or reset.
        template< typename T , typename Deleter = deleter<T> > class
shared_ptr : public base_ptr< T , Deleter >
        {
        public:

                typedef T element_type;

                explicit shared_ptr(T* p =0,const Deleter& d = deleter<T>()) :
base_ptr<T,Deleter>(p,d)
                {
                         try
                         {
                                pn = new long(1);
                         } // fix: prevent leak if new throws
                         catch (...)
                         {
                                 del();
                                 throw;
                         }
                 }

                ~shared_ptr() { dispose(); }
    
                shared_ptr(const shared_ptr& r) throw() : base_ptr<T,Deleter>(r)
                {
                         ++*(pn = r.pn);
                }

                shared_ptr(std::auto_ptr<T>& r,const Deleter& d = deleter<T>()) :
base_ptr<T,Deleter>(NULL,d)
                {
                         pn = new long(1); // may throw
                         ptr() = r.release(); // fix: moved here to stop leak if new throws
                }

                shared_ptr& operator=(const shared_ptr& r)
                {
                         share(r.get(),r.pn);
                         return *this;
                }

                shared_ptr& operator=(std::auto_ptr<T>& r)
                {
                         // code choice driven by guarantee of "no effect if new throws"
                         if (*pn == 1)
                         {
                                 deleter(px);
                         }
                         else
                         {
                                // allocate new reference counter
                                long * tmp = new long(1); // may throw
                                --*pn; // only decrement once danger of new throwing is past
                                pn = tmp;
                         } // allocate new reference counter
                         ptr() = r.release(); // fix: moved here so doesn't leak if new
throws
                         return *this;
                }

                void reset(T* p=0)
                {
                        if ( ptr() == p )
                                return; // fix: self-assignment safe
                        if (--*pn == 0)
                        {
                                del();
                        }
                        else
                        { // allocate new reference counter
                                try
                                {
                                        pn = new long;
                                } // fix: prevent leak if new throws
                                catch (...)
                                {
                                  ++*pn; // undo effect of --*pn above to meet effects guarantee
                                  del(p);
                                  throw;
                                } // catch
                        } // allocate new reference counter
                        *pn = 1;
                        ptr() = p;
                } // reset

           long use_count() const throw(){ return *pn; }
           bool unique() const throw() { return *pn == 1; }

           T* release() throw()
           {
                   *pn = 0;
                   T* ret = get();
                   ptr() = NULL;
                   return ret;
           }

           void swap(shared_ptr<T>& other) throw()
           {
                   std::swap(ptr(),other.ptr());
                   std::swap(pn,other.pn);
           }

        protected:

                long* pn; // Counter
                
                void dispose() { if (--*pn == 0) { del(); delete pn; } }
                
                void share(T* rpx, long* rpn)
                {
                        if (pn != rpn)
                        {
                                dispose();
                                ptr() = rpx;
                                ++*(pn = rpn);
                        }
                } // share
        }; // shared_ptr

        template <class T,class Deleter = array_deleter<T> > class
shared_array : public shared_ptr<T,Deleter>
        {
        public:
                explicit shared_array(T* p =0,const Deleter& d = array_deleter<T>())
: shared_ptr<T,Deleter>(p,d) {};
                ~shared_array() { dispose(); }
                T& operator[](size_t i) const throw() { return ptr()[i]; }
        };
}


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