diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index 4e6b4ec81..d174b9753 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -871,6 +871,7 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) # C++ tests SET(KWSYS_CXX_TESTS + testAutoPtr testHashSTL testRegistry testIOS diff --git a/Source/kwsys/auto_ptr.hxx.in b/Source/kwsys/auto_ptr.hxx.in index e93183568..89a9de665 100644 --- a/Source/kwsys/auto_ptr.hxx.in +++ b/Source/kwsys/auto_ptr.hxx.in @@ -19,46 +19,150 @@ namespace @KWSYS_NAMESPACE@ template class auto_ptr; +namespace detail +{ // The auto_ptr_ref template is supposed to be a private member of -// auto_ptr but Borland 5.8 cannot handle it. The extra constructor -// argument prevents implicit conversion to auto_ptr_ref from auto_ptr -// through the constructor. This avoids problems on Borland compilers -// when returning auto_ptr by value from a function. +// auto_ptr but Borland 5.8 cannot handle it. Instead put it in +// a private namespace. template struct auto_ptr_ref { auto_ptr& p_; + + // The extra constructor argument prevents implicit conversion to + // auto_ptr_ref from auto_ptr through the constructor. Normally + // this should be done with the explicit keyword but Borland 5.x + // generates code in the conversion operator to call itself + // infinately. auto_ptr_ref(auto_ptr& p, int): p_(p) {} }; +} -// C++98 Standard Section 20.4.5 - Template class auto_ptr. +/** C++98 Standard Section 20.4.5 - Template class auto_ptr. */ template class auto_ptr { X* x_; public: + /** The type of object held by the auto_ptr. */ typedef X element_type; + /** Construct from an auto_ptr holding a compatible object. This + transfers ownership to the newly constructed auto_ptr. */ template - auto_ptr(auto_ptr& a) throw(): x_(a.release()) {} + auto_ptr(auto_ptr& a) throw(): x_(a.release()) + { + } + + /** Assign from an auto_ptr holding a compatible object. This + transfers ownership to the left-hand-side of the assignment. */ template auto_ptr& operator=(auto_ptr& a) throw() - { reset(a.release()); return *this; } + { + this->reset(a.release()); + return *this; + } - explicit auto_ptr(X* p=0) throw(): x_(p) {} - auto_ptr(auto_ptr& a) throw(): x_(a.release()) {} - auto_ptr& operator=(auto_ptr& a) throw() { reset(a.release()); return *this; } - ~auto_ptr() throw() { delete get(); } + /** + * Explicitly construct from a raw pointer. This is typically + * called with the result of operator new. For example: + * + * auto_ptr ptr(new X()); + */ + explicit auto_ptr(X* p=0) throw(): x_(p) + { + } - X& operator*() const throw() { return *get(); } - X* operator->() const throw() { return get(); } - X* get() const throw() { return x_; } - X* release() throw() { X* x = x_; x_ = 0; return x; } - void reset(X* p=0) throw() { if(get() != p) { delete get(); x_ = p; } } + /** Construct from another auto_ptr holding an object of the same + type. This transfers ownership to the newly constructed + auto_ptr. */ + auto_ptr(auto_ptr& a) throw(): x_(a.release()) + { + } - auto_ptr(auto_ptr_ref r) throw(): x_(r.p_.release()) {} - template operator auto_ptr_ref() throw() { return auto_ptr_ref(*this, 1); } - template operator auto_ptr() throw() { return release(); } - auto_ptr& operator=(auto_ptr_ref r) throw() { reset(r.p_.release()); return *this; } + /** Assign from another auto_ptr holding an object of the same type. + This transfers ownership to the newly constructed auto_ptr. */ + auto_ptr& operator=(auto_ptr& a) throw() + { + this->reset(a.release()); + return *this; + } + + /** Destruct and delete the object held. */ + ~auto_ptr() throw() + { + // Assume object destructor is nothrow. + delete this->x_; + } + + /** Dereference and return a reference to the object held. */ + X& operator*() const throw() + { + return *this->x_; + } + + /** Return a pointer to the object held. */ + X* operator->() const throw() + { + return this->x_; + } + + /** Return a pointer to the object held. */ + X* get() const throw() + { + return this->x_; + } + + /** Return a pointer to the object held and reset to hold no object. + This transfers ownership to the caller. */ + X* release() throw() + { + X* x = this->x_; + this->x_ = 0; + return x; + } + + /** Assume ownership of the given object. The object previously + held is deleted. */ + void reset(X* p=0) throw() + { + if(this->x_ != p) + { + // Assume object destructor is nothrow. + delete this->x_; + this->x_ = p; + } + } + + /** Construct from an auto_ptr_ref. This is used when the + constructor argument is a call to a function returning an + auto_ptr. */ + auto_ptr(detail::auto_ptr_ref r) throw(): x_(r.p_.release()) + { + } + + /** Convert to an auto_ptr_ref. This is used when a function + returning an auto_ptr is the argument to the constructor of + another auto_ptr. */ + template operator detail::auto_ptr_ref() throw() + { + return detail::auto_ptr_ref(*this, 1); + } + + /** Convert to an auto_ptr holding an object of a compatible type. + This transfers ownership to the returned auto_ptr. */ + template operator auto_ptr() throw() + { + return auto_ptr(this->release()); + } + + /** Assign from an auto_ptr_ref. This is used when a function + returning an auto_ptr is passed on the right-hand-side of an + assignment. */ + auto_ptr& operator=(detail::auto_ptr_ref r) throw() + { + this->reset(r.p_.release()); + return *this; + } }; } // namespace @KWSYS_NAMESPACE@ diff --git a/Source/kwsys/testAutoPtr.cxx b/Source/kwsys/testAutoPtr.cxx new file mode 100644 index 000000000..5afa5994b --- /dev/null +++ b/Source/kwsys/testAutoPtr.cxx @@ -0,0 +1,166 @@ +/*========================================================================= + + Program: KWSys - Kitware System Library + Module: $RCSfile$ + + Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "kwsysPrivate.h" +#include KWSYS_HEADER(auto_ptr.hxx) + +// Work-around CMake dependency scanning limitation. This must +// duplicate the above list of headers. +#if 0 +# include "auto_ptr.hxx.in" +#endif + +#include + +#define ASSERT(x,y) if (!(x)) { printf("FAIL: " y "\n"); status = 1; } + +static int instances = 0; + +struct A +{ + A() { ++instances; } + ~A() { --instances; } + A* self() {return this; } +}; +struct B: public A {}; + +static int function_call(kwsys::auto_ptr a) +{ + return a.get()? 1:0; +} + +static A* get_A(A& a) { return &a; } + +static kwsys::auto_ptr generate_auto_ptr_A() +{ + return kwsys::auto_ptr(new A); +} + +static kwsys::auto_ptr generate_auto_ptr_B() +{ + return kwsys::auto_ptr(new B); +} + +int testAutoPtr(int, char*[]) +{ + int status = 0; + + // Keep everything in a subscope so we can detect leaks. + { + kwsys::auto_ptr pa0; + kwsys::auto_ptr pa1(new A()); + kwsys::auto_ptr pb1(new B()); + kwsys::auto_ptr pb2(new B()); + kwsys::auto_ptr pa2(new B()); + + A* ptr = get_A(*pa1); + ASSERT(ptr == pa1.get(), + "auto_ptr does not return correct object when dereferenced"); + ptr = pa1->self(); + ASSERT(ptr == pa1.get(), + "auto_ptr does not return correct pointer from operator->"); + + A* before = pa0.get(); + pa0.reset(new A()); + ASSERT(pa0.get() && pa0.get() != before, + "auto_ptr empty after reset(new A())"); + + before = pa0.get(); + pa0.reset(new B()); + ASSERT(pa0.get() && pa0.get() != before, + "auto_ptr empty after reset(new B())"); + + delete pa0.release(); + ASSERT(!pa0.get(), "auto_ptr holds an object after release()"); + + kwsys::auto_ptr pa3(pb1); + ASSERT(!pb1.get(), + "auto_ptr full after being used to construct another"); + ASSERT(pa3.get(), + "auto_ptr empty after construction from another"); + + { + kwsys::auto_ptr pa; + pa = pa3; + ASSERT(!pa3.get(), + "auto_ptr full after assignment to another"); + ASSERT(pa.get(), + "auto_ptr empty after assignment from another"); + } + + { + kwsys::auto_ptr pa; + pa = pb2; + ASSERT(!pb2.get(), + "auto_ptr full after assignment to compatible"); + ASSERT(pa.get(), + "auto_ptr empty after assignment from compatible"); + } + + { + int receive = function_call(pa2); + ASSERT(receive, + "auto_ptr did not receive ownership in called function"); + ASSERT(!pa2.get(), + "auto_ptr did not release ownership to called function"); + } + + { + int received = function_call(generate_auto_ptr_A()); + ASSERT(received, + "auto_ptr in called function did not take ownership " + "from factory function"); + } + + { + int received = function_call(generate_auto_ptr_B()); + ASSERT(received, + "auto_ptr in called function did not take ownership " + "from factory function with conversion"); + } + + { + kwsys::auto_ptr pa(generate_auto_ptr_A()); + ASSERT(pa.get(), + "auto_ptr empty after construction from factory function"); + } + + { + kwsys::auto_ptr pa; + pa = generate_auto_ptr_A(); + ASSERT(pa.get(), + "auto_ptr empty after assignment from factory function"); + } + +#if 0 + { + kwsys::auto_ptr pa(generate_auto_ptr_B()); + ASSERT(pa.get(), + "auto_ptr empty after construction from compatible factory function"); + } +#endif + +#if 0 + { + kwsys::auto_ptr pa; + pa = generate_auto_ptr_B(); + ASSERT(pa.get(), + "auto_ptr empty after assignment from compatible factory function"); + } +#endif + } + + ASSERT(instances == 0, "auto_ptr leaked an object"); + + return status; +}