|
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