Boost logo

Boost :

From: Greg Colvin (gcolvin_at_[hidden])
Date: 2000-10-02 15:26:47


> When I needed such a class, I created auto_array_ptr from auto_ptr (cut &
> paste) and changed delete to delete [].
>
> Inelegant? yes.
> Quick & painless? yes.
> Did it work? yes.
> Would I rather see a version with a parameterized deleter function? Yes.

I just tried that, but discovered in testing that it would incorrectly
convert from Derived[] to Base[].

Doh.

So parameterization on the deleter function is not enough for a correct
auto_array_ptr. What it seems to take is to remove the templatization of
the constructor, assignment, and auto_ptr_ref members. Following is the
code so far, which (gasp! shudder!) requires a conforming C++ compiler.
I've tested with EDG. Boostification is left as an exercise for someone
with more patience with Microsoft than I can muster amymore.

///////////////////////////////////////////////////////////////////////////////
namespace boost {

   class auto_ptr_base {
      template<typename Y> friend struct auto_ptr;
      template<typename Y> friend struct auto_array_ptr;
      template<typename Y> friend struct auto_ptr_ref;
      void* p;
   };

   template <typename X> class auto_ptr_ref {
      template<typename Y> friend struct auto_ptr;
      template<typename Y> friend struct auto_array_ptr;
      auto_ptr_ref(X* p, auto_ptr_base& r) : r(r), p(p) {}
      X* release() const { r.p = 0; return p; }
      auto_ptr_base& r;
      X* const p;
   };

   template<typename X> struct auto_ptr : auto_ptr_base {
      typedef X element_type;

      explicit auto_ptr(X* px =0) throw() { p = px; }
      auto_ptr(auto_ptr& r) throw() { p = (void*)r.release(); }
      template<typename Y> auto_ptr(auto_ptr<Y>& r) throw() {
         X* px = r.release();
         p = (void*)px;
      }
      auto_ptr& operator=(auto_ptr& r) throw() {
         reset(r.release());
         return *this;
      }
      template<typename Y> auto_ptr& operator=(auto_ptr<Y>& r) throw() {
         reset(r.release());
         return *this;
      }
      ~auto_ptr() { delete get(); }

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

      X* get() const throw() { return static_cast<X*>(p); }
      X* release() throw() { X* px = get(); p = 0; return px; }
      void reset(X* px=0) throw() { if (px != get()) delete get(), p = (void*)px; }

      auto_ptr(auto_ptr_ref<X> r) throw() {
         p = (void*)r.release();
      }
      auto_ptr& operator=(auto_ptr_ref<X> r) throw() {
         reset(r.release());
         return *this;
      }
      template<typename Y> operator auto_ptr_ref<Y>() throw() {
         return auto_ptr_ref<Y>(get(),*this);
      }
      template<typename Y> operator auto_ptr<Y>() throw() {
         return auto_ptr<Y>(release());
      }
   };
   
   template<typename X> struct auto_array_ptr : auto_ptr_base {
      typedef X element_type;

      explicit auto_array_ptr(X* px =0) throw() { p = px; }
      auto_array_ptr(auto_array_ptr& r) throw() { p = (void*)r.release(); }
      auto_array_ptr& operator=(auto_array_ptr& r) throw() {
         reset(r.release());
         return *this;
      }
      ~auto_array_ptr() { delete[] get(); }

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

      X* get() const throw() { return static_cast<X*>(p); }
      X* release() throw() { X* px = get(); p = 0; return px; }
      void reset(X* px=0) throw() { if (px != get()) delete[] get(), p = (void*)px; }

      auto_array_ptr(auto_ptr_ref<X> r) throw() {
         p = (void*)r.release();
      }
      auto_array_ptr& operator=(auto_ptr_ref<X> r) throw() {
         reset(r.release());
         return *this;
      }
      operator auto_ptr_ref<X>() throw() {
         return auto_ptr_ref<X>(get(),*this);
      }

   };
}

///////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
using namespace boost;

struct MemoryTracker
{
    MemoryTracker(void* newMem)
        : memory(newMem), next(list) { list = this; }

    static bool StopTracking(void* oldMem) {
        for ( MemoryTracker* p = list; p != 0; p = p->next ) {
            if ( p->memory == oldMem ) {
                p->memory = 0;
                return true;
            }
        }
        ++nFailure;
        puts( "Bad delete" );
        return false;
    }

    static int CheckAllDeleted() {
        for ( MemoryTracker* p = list; p != 0; p = p->next ) {
            if ( p->memory != 0 )
                ++nFailure, puts("not deleted");
        }
        return nFailure;
    }

private:
   void* memory;
   MemoryTracker* next;
   static int nFailure;
   static MemoryTracker* list; // linked list of all memory trackers
};

int MemoryTracker::nFailure = 0;
MemoryTracker* MemoryTracker::list = 0;

struct Base
{
   virtual ~Base() {} // So we can delete a Derived through a Base*

   // To test passing auto_ptr<Derived> as auto_ptr<Base>
   static void sink(auto_ptr<Base>) {}

   void* operator new(unsigned n) {
      void* p = ::operator new(n);
      new MemoryTracker(p);
      return p;
   }

   void operator delete(void* p) {
      if (MemoryTracker::StopTracking( p ));
         ::operator delete(p);
   }

   char dummy; // force this class to occupy space
};

// A dummy base class
struct ForceOffset {
   char dummy; // force this class to occupy space
};

// Trying to force Derived and Base to have different addresses
struct Derived : ForceOffset, Base {
   // To test passing auto_ptr<Derived> as auto_ptr<Derived>
   static void sink(auto_ptr<Derived>) {}
   static void sink(auto_array_ptr<Derived>) {}

   char dummy; // force this class to occupy space
};

auto_ptr<Derived> source() {
   return auto_ptr<Derived>(new Derived);
}

void test() {
   // Test functionality with no conversions
   auto_ptr<const Derived> p(source());
   auto_ptr<const Derived> pp(p);
   Derived::sink(source());
   p = pp;
   p = source();

   // Test functionality with Derived->base conversions
   auto_ptr<const Base> q(source());
   auto_ptr<const Base> qp(p);
// Base::sink(source()); // committee decided to leave this one broken
   q = pp;
   q = source();
}

auto_array_ptr<Derived> array_source() {
   return auto_array_ptr<Derived>(new Derived[3]);
}

void array_test() {

   // Test functionality with no conversions
   auto_array_ptr<Derived> p(array_source());
   auto_array_ptr<Derived> pp(p);
   Derived::sink(array_source());
   p = pp;
   p = array_source();

   // Test functionality with Derived->base conversions
#if 1 // shouldn't compile, reset to 0 for runtime testing
    auto_array_ptr<Base> q(array_source());
    auto_array_ptr<Base> qp(p);
    q = pp;
    q = array_source();
#endif

}

int main() {
   test();
   array_test();
   return MemoryTracker::CheckAllDeleted();
}


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