From 468c41041b3d7ec0394cea52e3c24b7ba90743f7 Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Fri, 7 Jul 2000 16:04:40 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r4, which included commits to RCS files with non-trunk default branches. [SVN r7621] --- include/boost/smart_ptr.hpp | 369 ++++++++++++++++++++++++++++++++++++ smart_ptr_test.cpp | 263 +++++++++++++++++++++++++ 2 files changed, 632 insertions(+) create mode 100644 include/boost/smart_ptr.hpp create mode 100644 smart_ptr_test.cpp diff --git a/include/boost/smart_ptr.hpp b/include/boost/smart_ptr.hpp new file mode 100644 index 0000000..e233104 --- /dev/null +++ b/include/boost/smart_ptr.hpp @@ -0,0 +1,369 @@ +// Boost smart_ptr.hpp header file -----------------------------------------// + +// (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. + +// See http://www.boost.org for most recent version including documentation. + +// Revision History +// 22 Jun 00 Remove #if continuations to fix GCC 2.95.2 problem (Beman Dawes) +// 1 Feb 00 Additional shared_ptr BOOST_NO_MEMBER_TEMPLATES workarounds +// (Dave Abrahams) +// 31 Dec 99 Condition tightened for no member template friend workaround +// (Dave Abrahams) +// 30 Dec 99 Moved BOOST_NMEMBER_TEMPLATES compatibility code to config.hpp +// (Dave Abrahams) +// 30 Nov 99 added operator ==, operator !=, and std::swap and std::less +// specializations for shared types (Darin Adler) +// 11 Oct 99 replaced op[](int) with op[](std::size_t) (Ed Brey, Valentin +// Bonnard), added shared_ptr workaround for no member template +// friends (Matthew Langston) +// 25 Sep 99 added shared_ptr::swap and shared_array::swap (Luis Coelho). +// 20 Jul 99 changed name to smart_ptr.hpp, #include , +// #include and use boost::noncopyable +// 17 May 99 remove scoped_array and shared_array operator*() as +// unnecessary (Beman Dawes) +// 14 May 99 reorder code so no effects when bad_alloc thrown (Abrahams/Dawes) +// 13 May 99 remove certain throw() specifiers to avoid generated try/catch +// code cost (Beman Dawes) +// 11 May 99 get() added, conversion to T* placed in macro guard (Valentin +// Bonnard, Dave Abrahams, and others argued for elimination +// of the automatic conversion) +// 28 Apr 99 #include fix (Valentin Bonnard) +// 28 Apr 99 rename transfer() to share() for clarity (Dave Abrahams) +// 28 Apr 99 remove unsafe shared_array template conversions(Valentin Bonnard) +// 28 Apr 99 p(r) changed to p(r.px) for clarity (Dave Abrahams) +// 21 Apr 99 reset() self assignment fix (Valentin Bonnard) +// 21 Apr 99 dispose() provided to improve clarity (Valentin Bonnard) +// 27 Apr 99 leak when new throws fixes (Dave Abrahams) +// 21 Oct 98 initial Version (Greg Colvin/Beman Dawes) + +#ifndef BOOST_SMART_PTR_HPP +#define BOOST_SMART_PTR_HPP + +#include // for broken compiler workarounds +#include // for std::size_t +#include // for std::auto_ptr +#include // for std::swap +#include // for boost::noncopyable +#include // for std::less + +namespace boost { + +// 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 class scoped_ptr : noncopyable { + + T* ptr; + + public: + typedef T element_type; + + explicit scoped_ptr( T* p=0 ) throw() : ptr(p) {} + ~scoped_ptr() { delete ptr; } + + void reset( T* p=0 ) { if ( ptr != p ) { delete ptr; ptr = p; } } + T& operator*() const throw() { return *ptr; } + T* operator->() const throw() { return ptr; } + T* get() const throw() { return ptr; } +#ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const throw() { return ptr; } +#endif + }; // 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 class scoped_array : noncopyable { + + T* ptr; + + public: + typedef T element_type; + + explicit scoped_array( T* p=0 ) throw() : ptr(p) {} + ~scoped_array() { delete [] ptr; } + + void reset( T* p=0 ) { if ( ptr != p ) {delete [] ptr; ptr=p;} } + + T* get() const throw() { return ptr; } +#ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const throw() { return ptr; } +#else + T& operator[](std::size_t i) const throw() { return ptr[i]; } +#endif + }; // 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 class shared_ptr { + public: + typedef T element_type; + + explicit shared_ptr(T* p =0) : px(p) { + try { pn = new long(1); } // fix: prevent leak if new throws + catch (...) { delete p; throw; } + } + + shared_ptr(const shared_ptr& r) throw() : px(r.px) { ++*(pn = r.pn); } + + ~shared_ptr() { dispose(); } + + shared_ptr& operator=(const shared_ptr& r) { + share(r.px,r.pn); + return *this; + } + +#if !defined( BOOST_NO_MEMBER_TEMPLATES ) + template + shared_ptr(const shared_ptr& r) throw() : px(r.px) { + ++*(pn = r.pn); + } + + template + shared_ptr(std::auto_ptr& r) { + pn = new long(1); // may throw + px = r.release(); // fix: moved here to stop leak if new throws + } + + template + shared_ptr& operator=(const shared_ptr& r) { + share(r.px,r.pn); + return *this; + } + + template + shared_ptr& operator=(std::auto_ptr& r) { + // code choice driven by guarantee of "no effect if new throws" + if (*pn == 1) { delete 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 + px = r.release(); // fix: moved here so doesn't leak if new throws + return *this; + } +#else + shared_ptr(std::auto_ptr& r) { + pn = new long(1); // may throw + px = r.release(); // fix: moved here to stop leak if new throws + } + + shared_ptr& operator=(std::auto_ptr& r) { + // code choice driven by guarantee of "no effect if new throws" + if (*pn == 1) { delete 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 + px = r.release(); // fix: moved here so doesn't leak if new throws + return *this; + } +#endif + + void reset(T* p=0) { + if ( px == p ) return; // fix: self-assignment safe + if (--*pn == 0) { delete px; } + 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 + delete p; + throw; + } // catch + } // allocate new reference counter + *pn = 1; + px = p; + } // reset + + T& operator*() const throw() { return *px; } + T* operator->() const throw() { return px; } + T* get() const throw() { return px; } + #ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const throw() { return px; } + #endif + + long use_count() const throw(){ return *pn; } + bool unique() const throw() { return *pn == 1; } + + void swap(shared_ptr& other) throw() + { std::swap(px,other.px); std::swap(pn,other.pn); } + +// Tasteless as this may seem, making all members public allows member templates +// to work in the absence of member template friends. (Matthew Langston) +// Don't split this line into two; that causes problems for some GCC 2.95.2 builds +#if defined(BOOST_NO_MEMBER_TEMPLATES) || !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) + private: +#endif + + T* px; // contained pointer + long* pn; // ptr to reference counter + +// Don't split this line into two; that causes problems for some GCC 2.95.2 builds +#if !defined( BOOST_NO_MEMBER_TEMPLATES ) && !defined( BOOST_NO_MEMBER_TEMPLATE_FRIENDS ) + template friend class shared_ptr; +#endif + + void dispose() { if (--*pn == 0) { delete px; delete pn; } } + + void share(T* rpx, long* rpn) { + if (pn != rpn) { + dispose(); + px = rpx; + ++*(pn = rpn); + } + } // share +}; // shared_ptr + +template + inline bool operator==(const shared_ptr& a, const shared_ptr& b) + { return a.get() == b.get(); } + +template + inline bool operator!=(const shared_ptr& a, const shared_ptr& b) + { return a.get() != b.get(); } + +// shared_array ------------------------------------------------------------// + +// shared_array extends shared_ptr to arrays. +// The array pointed to is deleted when the last shared_array pointing to it +// is destroyed or reset. + +template class shared_array { + public: + typedef T element_type; + + explicit shared_array(T* p =0) : px(p) { + try { pn = new long(1); } // fix: prevent leak if new throws + catch (...) { delete [] p; throw; } + } + + shared_array(const shared_array& r) throw() : px(r.px) { ++*(pn = r.pn); } + + ~shared_array() { dispose(); } + + shared_array& operator=(const shared_array& r) { + if (pn != r.pn) { + dispose(); + px = r.px; + ++*(pn = r.pn); + } + return *this; + } // operator= + + void reset(T* p=0) { + if ( px == p ) return; // fix: self-assignment safe + if (--*pn == 0) { delete [] px; } + 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 + delete [] p; + throw; + } // catch + } // allocate new reference counter + *pn = 1; + px = p; + } // reset + + T* get() const throw() { return px; } + #ifdef BOOST_SMART_PTR_CONVERSION + // get() is safer! Define BOOST_SMART_PTR_CONVERSION at your own risk! + operator T*() const throw() { return px; } + #else + T& operator[](std::size_t i) const throw() { return px[i]; } + #endif + + long use_count() const throw() { return *pn; } + bool unique() const throw() { return *pn == 1; } + + void swap(shared_array& other) throw() + { std::swap(px,other.px); std::swap(pn,other.pn); } + + private: + + T* px; // contained pointer + long* pn; // ptr to reference counter + + void dispose() { if (--*pn == 0) { delete [] px; delete pn; } } + +}; // shared_array + +template + inline bool operator==(const shared_array& a, const shared_array& b) + { return a.get() == b.get(); } + +template + inline bool operator!=(const shared_array& a, const shared_array& b) + { return a.get() != b.get(); } + +} // namespace boost + +// specializations for things in namespace std -----------------------------// + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +namespace std { + +// Specialize std::swap to use the fast, non-throwing swap that's provided +// as a member function instead of using the default algorithm which creates +// a temporary and uses assignment. + +template + inline void swap(boost::shared_ptr& a, boost::shared_ptr& b) + { a.swap(b); } + +template + inline void swap(boost::shared_array& a, boost::shared_array& b) + { a.swap(b); } + +// Specialize std::less so we can use shared pointers and arrays as keys in +// associative collections. + +// It's still a controversial question whether this is better than supplying +// a full range of comparison operators (<, >, <=, >=). + +template + struct less< boost::shared_ptr > + : binary_function, boost::shared_ptr, bool> + { + bool operator()(const boost::shared_ptr& a, + const boost::shared_ptr& b) const + { return less()(a.get(),b.get()); } + }; + +template + struct less< boost::shared_array > + : binary_function, boost::shared_array, bool> + { + bool operator()(const boost::shared_array& a, + const boost::shared_array& b) const + { return less()(a.get(),b.get()); } + }; + +} // namespace std + +#endif // ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +#endif // BOOST_SMART_PTR_HPP + diff --git a/smart_ptr_test.cpp b/smart_ptr_test.cpp new file mode 100644 index 0000000..cd4d61b --- /dev/null +++ b/smart_ptr_test.cpp @@ -0,0 +1,263 @@ +// smart pointer test program ----------------------------------------------// + +// (C) Copyright 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. + +// Revision History +// 29 Nov 99 added std::swap and associative container tests (Darin Adler) +// 25 Sep 99 added swap tests +// 20 Jul 99 header name changed to .hpp +// 20 Apr 99 additional error tests added. + +#include +#include +#include +#include +#include + +#ifdef NDEBUG +#error This test program makes no sense if NDEBUG is defined +#endif + +using namespace std; +using boost::scoped_ptr; +using boost::scoped_array; +using boost::shared_ptr; +using boost::shared_array; + +template +void ck( const T* v1, T v2 ) { assert( *v1 == v2 ); } + +namespace { + int UDT_use_count; // independent of pointer maintained counts + } + +// user defined type -------------------------------------------------------// + +class UDT { + long value_; + public: + explicit UDT( long value=0 ) : value_(value) { ++UDT_use_count; } + ~UDT() { + --UDT_use_count; + cout << "UDT with value " << value_ << " being destroyed" << endl; + } + long value() const { return value_; } + void value( long v ) { value_ = v;; } + }; // UDT + +// main --------------------------------------------------------------------// + +// This isn't a very systematic test; it just hits some of the basics. + +int main() { + + assert( UDT_use_count == 0 ); // reality check + + // test scoped_ptr with a built-in type + long * lp = new long; + scoped_ptr sp ( lp ); + assert( sp.get() == lp ); + assert( lp == sp.get() ); + assert( &*sp == lp ); + + *sp = 1234568901L; + assert( *sp == 1234568901L ); + assert( *lp == 1234568901L ); + ck( static_cast(sp.get()), 1234568901L ); + ck( lp, *sp ); + + sp.reset(); + assert( sp.get() == 0 ); + + // test scoped_ptr with a user defined type + scoped_ptr udt_sp ( new UDT( 999888777 ) ); + assert( udt_sp->value() == 999888777 ); + udt_sp.reset(); + udt_sp.reset( new UDT( 111222333 ) ); + assert( udt_sp->value() == 111222333 ); + udt_sp.reset( new UDT( 333222111 ) ); + assert( udt_sp->value() == 333222111 ); + + // test scoped_array with a build-in type + char * sap = new char [ 100 ]; + scoped_array sa ( sap ); + assert( sa.get() == sap ); + assert( sap == sa.get() ); + + strcpy( sa.get(), "Hot Dog with mustard and relish" ); + assert( strcmp( sa.get(), "Hot Dog with mustard and relish" ) == 0 ); + assert( strcmp( sap, "Hot Dog with mustard and relish" ) == 0 ); + + assert( sa[0] == 'H' ); + assert( sa[30] == 'h' ); + + sa[0] = 'N'; + sa[4] = 'd'; + assert( strcmp( sap, "Not dog with mustard and relish" ) == 0 ); + + sa.reset(); + assert( sa.get() == 0 ); + + // test shared_ptr with a built-in type + int * ip = new int; + shared_ptr cp ( ip ); + assert( ip == cp.get() ); + assert( cp.use_count() == 1 ); + + *cp = 54321; + assert( *cp == 54321 ); + assert( *ip == 54321 ); + ck( static_cast(cp.get()), 54321 ); + ck( static_cast(ip), *cp ); + + shared_ptr cp2 ( cp ); + assert( ip == cp2.get() ); + assert( cp.use_count() == 2 ); + assert( cp2.use_count() == 2 ); + + assert( *cp == 54321 ); + assert( *cp2 == 54321 ); + ck( static_cast(cp2.get()), 54321 ); + ck( static_cast(ip), *cp2 ); + + shared_ptr cp3 ( cp ); + assert( cp.use_count() == 3 ); + assert( cp2.use_count() == 3 ); + assert( cp3.use_count() == 3 ); + cp.reset(); + assert( cp2.use_count() == 2 ); + assert( cp3.use_count() == 2 ); + assert( cp.use_count() == 1 ); + cp.reset( new int ); + *cp = 98765; + assert( *cp == 98765 ); + *cp3 = 87654; + assert( *cp3 == 87654 ); + assert( *cp2 == 87654 ); + cp.swap( cp3 ); + assert( *cp == 87654 ); + assert( *cp2 == 87654 ); + assert( *cp3 == 98765 ); + cp.swap( cp3 ); + assert( *cp == 98765 ); + assert( *cp2 == 87654 ); + assert( *cp3 == 87654 ); + cp2 = cp2; + assert( cp2.use_count() == 2 ); + assert( *cp2 == 87654 ); + cp = cp2; + assert( cp2.use_count() == 3 ); + assert( *cp2 == 87654 ); + assert( cp.use_count() == 3 ); + assert( *cp == 87654 ); + + shared_ptr cp4; + swap( cp2, cp4 ); + assert( cp4.use_count() == 3 ); + assert( *cp4 == 87654 ); + assert( cp2.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_ptr > scp; + scp.insert(cp4); + assert( scp.find(cp4) != scp.end() ); + assert( scp.find(cp4) == scp.find( shared_ptr(cp4) ) ); +#endif + + // test shared_array with a built-in type + char * cap = new char [ 100 ]; + shared_array ca ( cap ); + assert( ca.get() == cap ); + assert( cap == ca.get() ); + assert( &ca[0] == cap ); + + strcpy( ca.get(), "Hot Dog with mustard and relish" ); + assert( strcmp( ca.get(), "Hot Dog with mustard and relish" ) == 0 ); + assert( strcmp( cap, "Hot Dog with mustard and relish" ) == 0 ); + + assert( ca[0] == 'H' ); + assert( ca[30] == 'h' ); + + shared_array ca2 ( ca ); + shared_array ca3 ( ca2 ); + + ca[0] = 'N'; + ca[4] = 'd'; + assert( strcmp( ca.get(), "Not dog with mustard and relish" ) == 0 ); + assert( strcmp( ca2.get(), "Not dog with mustard and relish" ) == 0 ); + assert( strcmp( ca3.get(), "Not dog with mustard and relish" ) == 0 ); + assert( ca.use_count() == 3 ); + assert( ca2.use_count() == 3 ); + assert( ca3.use_count() == 3 ); + ca2.reset(); + assert( ca.use_count() == 2 ); + assert( ca3.use_count() == 2 ); + assert( ca2.use_count() == 1 ); + + ca.reset(); + assert( ca.get() == 0 ); + + shared_array ca4; + swap( ca3, ca4 ); + assert( ca4.use_count() == 1 ); + assert( strcmp( ca4.get(), "Not dog with mustard and relish" ) == 0 ); + assert( ca3.get() == 0 ); + +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + set< shared_array > sca; + sca.insert(ca4); + assert( sca.find(ca4) != sca.end() ); + assert( sca.find(ca4) == sca.find( shared_array(ca4) ) ); +#endif + + // test shared_array with user defined type + shared_array udta ( new UDT[3] ); + + udta[0].value( 111 ); + udta[1].value( 222 ); + udta[2].value( 333 ); + shared_array udta2 ( udta ); + + assert( udta[0].value() == 111 ); + assert( udta[1].value() == 222 ); + assert( udta[2].value() == 333 ); + assert( udta2[0].value() == 111 ); + assert( udta2[1].value() == 222 ); + assert( udta2[2].value() == 333 ); + udta2.reset(); + assert( udta2.get() == 0 ); + assert( udta.use_count() == 1 ); + assert( udta2.use_count() == 1 ); + + assert( UDT_use_count == 4 ); // reality check + + // test shared_ptr with a user defined type + UDT * up = new UDT; + shared_ptr sup ( up ); + assert( up == sup.get() ); + assert( sup.use_count() == 1 ); + + sup->value( 54321 ) ; + assert( sup->value() == 54321 ); + assert( up->value() == 54321 ); + + shared_ptr sup2; + sup2 = sup; + assert( sup2->value() == 54321 ); + assert( sup.use_count() == 2 ); + assert( sup2.use_count() == 2 ); + sup2 = sup2; + assert( sup2->value() == 54321 ); + assert( sup.use_count() == 2 ); + assert( sup2.use_count() == 2 ); + + cout << "OK" << endl; + + new char[12345]; // deliberate memory leak to verify leaks detected + + return 0; + } // main +