//////////////////////////////////////////////////////////////////////////////// // The Loki Library // Copyright (c) 2001 by Andrei Alexandrescu // This code accompanies the book: // Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design // Patterns Applied". Copyright (c) 2001. Addison-Wesley. // Permission to use, copy, modify, distribute and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author or Addison-Wesley Longman make no representations about the // suitability of this software for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// #ifndef LOKI_SMARTPTR_INC_ #define LOKI_SMARTPTR_INC_ // $Id$ /// \defgroup SmartPointerGroup Smart pointers /// Policy based implementation of a smart pointer /// \defgroup SmartPointerOwnershipGroup Ownership policies /// \ingroup SmartPointerGroup /// \defgroup SmartPointerStorageGroup Storage policies /// \ingroup SmartPointerGroup /// \defgroup SmartPointerConversionGroup Conversion policies /// \ingroup SmartPointerGroup /// \defgroup SmartPointerCheckingGroup Checking policies /// \ingroup SmartPointerGroup #include #include #include #include #include #include #include #include #include #include #if !defined(_MSC_VER) # if defined(__sparc__) # include # else # include # endif #endif #if defined(_MSC_VER) || defined(__GNUC__) // GCC>=4.1 must use -ffriend-injection due to a bug in GCC #define LOKI_ENABLE_FRIEND_TEMPLATE_TEMPLATE_PARAMETER_WORKAROUND #endif #if defined( _MSC_VER ) #pragma warning( push ) #pragma warning( disable: 4355 ) #endif namespace Loki { //////////////////////////////////////////////////////////////////////////////// /// \class HeapStorage /// /// \ingroup SmartPointerStorageGroup /// Implementation of the StoragePolicy used by SmartPtr. Uses explicit call /// to T's destructor followed by call to free. //////////////////////////////////////////////////////////////////////////////// template class HeapStorage { public: typedef T* StoredType; /// the type of the pointee_ object typedef T* InitPointerType; /// type used to declare OwnershipPolicy type. typedef T* PointerType; /// type returned by operator-> typedef T& ReferenceType; /// type returned by operator* protected: HeapStorage() : pointee_(Default()) {} // The storage policy doesn't initialize the stored pointer // which will be initialized by the OwnershipPolicy's Clone fn HeapStorage(const HeapStorage&) : pointee_(0) {} template HeapStorage(const HeapStorage&) : pointee_(0) {} explicit HeapStorage(const StoredType& p) : pointee_(p) {} PointerType operator->() const { return pointee_; } ReferenceType operator*() const { return *pointee_; } void Swap(HeapStorage& rhs) { std::swap(pointee_, rhs.pointee_); } // Accessors template friend typename HeapStorage::PointerType GetImpl(const HeapStorage& sp); template friend const typename HeapStorage::StoredType& GetImplRef(const HeapStorage& sp); template friend typename HeapStorage::StoredType& GetImplRef(HeapStorage& sp); // Destroys the data stored // (Destruction might be taken over by the OwnershipPolicy) void Destroy() { if ( 0 != pointee_ ) { pointee_->~T(); ::free( pointee_ ); } } // Default value to initialize the pointer static StoredType Default() { return 0; } private: // Data StoredType pointee_; }; template inline typename HeapStorage::PointerType GetImpl(const HeapStorage& sp) { return sp.pointee_; } template inline const typename HeapStorage::StoredType& GetImplRef(const HeapStorage& sp) { return sp.pointee_; } template inline typename HeapStorage::StoredType& GetImplRef(HeapStorage& sp) { return sp.pointee_; } //////////////////////////////////////////////////////////////////////////////// /// \class DefaultSPStorage /// /// \ingroup SmartPointerStorageGroup /// Implementation of the StoragePolicy used by SmartPtr //////////////////////////////////////////////////////////////////////////////// template class DefaultSPStorage { public: typedef T* StoredType; // the type of the pointee_ object typedef T* InitPointerType; /// type used to declare OwnershipPolicy type. typedef T* PointerType; // type returned by operator-> typedef T& ReferenceType; // type returned by operator* protected: DefaultSPStorage() : pointee_(Default()) {} // The storage policy doesn't initialize the stored pointer // which will be initialized by the OwnershipPolicy's Clone fn DefaultSPStorage(const DefaultSPStorage&) : pointee_(0) {} template DefaultSPStorage(const DefaultSPStorage&) : pointee_(0) {} explicit DefaultSPStorage(const StoredType& p) : pointee_(p) {} PointerType operator->() const { return pointee_; } ReferenceType operator*() const { return *pointee_; } void Swap(DefaultSPStorage& rhs) { std::swap(pointee_, rhs.pointee_); } // Accessors template friend typename DefaultSPStorage::PointerType GetImpl(const DefaultSPStorage& sp); template friend const typename DefaultSPStorage::StoredType& GetImplRef(const DefaultSPStorage& sp); template friend typename DefaultSPStorage::StoredType& GetImplRef(DefaultSPStorage& sp); // Destroys the data stored // (Destruction might be taken over by the OwnershipPolicy) // // If your compiler gives you a warning in this area while // compiling the tests, it is on purpose, please ignore it. void Destroy() { delete pointee_; } // Default value to initialize the pointer static StoredType Default() { return 0; } private: // Data StoredType pointee_; }; template inline typename DefaultSPStorage::PointerType GetImpl(const DefaultSPStorage& sp) { return sp.pointee_; } template inline const typename DefaultSPStorage::StoredType& GetImplRef(const DefaultSPStorage& sp) { return sp.pointee_; } template inline typename DefaultSPStorage::StoredType& GetImplRef(DefaultSPStorage& sp) { return sp.pointee_; } //////////////////////////////////////////////////////////////////////////////// /// \class LockedStorage /// /// \ingroup SmartPointerStorageGroup /// Implementation of the StoragePolicy used by SmartPtr. /// /// Each call to operator-> locks the object for the duration of a call to a /// member function of T. /// /// \par How It Works /// LockedStorage has a helper class called Locker, which acts as a smart /// pointer with limited abilities. LockedStorage::operator-> returns an /// unnamed temporary of type Locker that exists for the duration of the /// call to a member function of T. The unnamed temporary locks the object /// when it is constructed by operator-> and unlocks the object when it is /// destructed. /// /// \note This storage policy requires class T to have member functions Lock /// and Unlock. If your class does not have Lock or Unlock functions, you may /// either make a child class which does, or make a policy class similar to /// LockedStorage which calls other functions to lock the object. //////////////////////////////////////////////////////////////////////////////// template class Locker { public: explicit Locker( const T * p ) : pointee_( const_cast< T * >( p ) ) { if ( pointee_ != 0 ) pointee_->Lock(); } ~Locker( void ) { if ( pointee_ != 0 ) pointee_->Unlock(); } operator T * () { return pointee_; } T * operator->() { return pointee_; } private: Locker( void ); Locker & operator = ( const Locker & ); T * pointee_; }; template class LockedStorage { public: typedef T* StoredType; /// the type of the pointee_ object typedef T* InitPointerType; /// type used to declare OwnershipPolicy type. typedef Locker< T > PointerType; /// type returned by operator-> typedef T& ReferenceType; /// type returned by operator* protected: LockedStorage() : pointee_( Default() ) {} ~LockedStorage( void ) {} LockedStorage( const LockedStorage&) : pointee_( 0 ) {} explicit LockedStorage( const StoredType & p ) : pointee_( p ) {} PointerType operator->() const { return Locker< T >( pointee_ ); } void Swap(LockedStorage& rhs) { std::swap( pointee_, rhs.pointee_ ); } // Accessors template friend typename LockedStorage::InitPointerType GetImpl(const LockedStorage& sp); template friend const typename LockedStorage::StoredType& GetImplRef(const LockedStorage& sp); template friend typename LockedStorage::StoredType& GetImplRef(LockedStorage& sp); // Destroys the data stored // (Destruction might be taken over by the OwnershipPolicy) void Destroy() { delete pointee_; } // Default value to initialize the pointer static StoredType Default() { return 0; } private: /// Dereference operator is not implemented. ReferenceType operator*(); // Data StoredType pointee_; }; template inline typename LockedStorage::InitPointerType GetImpl(const LockedStorage& sp) { return sp.pointee_; } template inline const typename LockedStorage::StoredType& GetImplRef(const LockedStorage& sp) { return sp.pointee_; } template inline typename LockedStorage::StoredType& GetImplRef(LockedStorage& sp) { return sp.pointee_; } namespace Private { //////////////////////////////////////////////////////////////////////////////// /// \class DeleteArrayBase /// /// \ingroup StrongPointerDeleteGroup /// Base class used only by the DeleteArray policy class. This stores the /// number of elements in an array of shared objects. //////////////////////////////////////////////////////////////////////////////// class DeleteArrayBase { public: inline size_t GetArrayCount( void ) const { return m_itemCount; } protected: DeleteArrayBase( void ) : m_itemCount( 0 ) {} explicit DeleteArrayBase( size_t itemCount ) : m_itemCount( itemCount ) {} DeleteArrayBase( const DeleteArrayBase & that ) : m_itemCount( that.m_itemCount ) {} void Swap( DeleteArrayBase & rhs ); void OnInit( const void * p ) const; void OnCheckRange( size_t index ) const; private: size_t m_itemCount; }; } //////////////////////////////////////////////////////////////////////////////// /// \class ArrayStorage /// /// \ingroup SmartPointerStorageGroup /// Implementation of the ArrayStorage used by SmartPtr //////////////////////////////////////////////////////////////////////////////// template class ArrayStorage : public ::Loki::Private::DeleteArrayBase { public: typedef T* StoredType; // the type of the pointee_ object typedef T* InitPointerType; /// type used to declare OwnershipPolicy type. typedef T* PointerType; // type returned by operator-> typedef T& ReferenceType; // type returned by operator* protected: ArrayStorage() : DeleteArrayBase(), pointee_(Default()) {} // The storage policy doesn't initialize the stored pointer // which will be initialized by the OwnershipPolicy's Clone fn ArrayStorage( const ArrayStorage & that ) : DeleteArrayBase( that ), pointee_( 0 ) {} template ArrayStorage( const ArrayStorage< U >& that ) : DeleteArrayBase( that ), pointee_( 0 ) {} ArrayStorage( const StoredType & p, size_t count ) : DeleteArrayBase( count ), pointee_( p ) {} PointerType operator->() const { return pointee_; } ReferenceType operator*() const { return *pointee_; } void Swap( ArrayStorage & rhs ) { DeleteArrayBase::Swap( rhs ); ::std::swap( pointee_, rhs.pointee_ ); } // Accessors template friend typename ArrayStorage::PointerType GetImpl(const ArrayStorage& sp); template friend const typename ArrayStorage::StoredType& GetImplRef(const ArrayStorage& sp); template friend typename ArrayStorage::StoredType& GetImplRef(ArrayStorage& sp); // Destroys the data stored // (Destruction might be taken over by the OwnershipPolicy) void Destroy() { delete [] pointee_; } // Default value to initialize the pointer static StoredType Default() { return 0; } private: // Data StoredType pointee_; }; template inline typename ArrayStorage::PointerType GetImpl(const ArrayStorage& sp) { return sp.pointee_; } template inline const typename ArrayStorage::StoredType& GetImplRef(const ArrayStorage& sp) { return sp.pointee_; } template inline typename ArrayStorage::StoredType& GetImplRef(ArrayStorage& sp) { return sp.pointee_; } //////////////////////////////////////////////////////////////////////////////// /// \class RefCounted /// /// \ingroup SmartPointerOwnershipGroup /// Implementation of the OwnershipPolicy used by SmartPtr /// Provides a classic external reference counting implementation //////////////////////////////////////////////////////////////////////////////// template class RefCounted { protected: RefCounted() : pCount_(static_cast( SmallObject<>::operator new(sizeof(uintptr_t)))) { assert(pCount_!=0); *pCount_ = 1; } RefCounted(const RefCounted& rhs) : pCount_(rhs.pCount_) {} // MWCW lacks template friends, hence the following kludge template RefCounted(const RefCounted& rhs) : pCount_(reinterpret_cast(rhs).pCount_) {} P Clone(const P& val) { ++*pCount_; return val; } bool Release(const P&) { if (!--*pCount_) { SmallObject<>::operator delete(pCount_, sizeof(uintptr_t)); pCount_ = NULL; return true; } return false; } void Swap(RefCounted& rhs) { std::swap(pCount_, rhs.pCount_); } enum { destructiveCopy = false }; private: // Data uintptr_t* pCount_; }; //////////////////////////////////////////////////////////////////////////////// /// \struct RefCountedMT /// /// \ingroup SmartPointerOwnershipGroup /// Implementation of the OwnershipPolicy used by SmartPtr /// Implements external reference counting for multithreaded programs /// Policy Usage: RefCountedMTAdj::RefCountedMT /// /// \par Warning /// There could be a race condition, see bug "Race condition in RefCountedMTAdj::Release" /// http://sourceforge.net/tracker/index.php?func=detail&aid=1408845&group_id=29557&atid=396644 /// As stated in bug 1408845, the Release function is not thread safe if a /// SmartPtr copy-constructor tries to copy the last pointer to an object in /// one thread, while the destructor is acting on the last pointer in another /// thread. The existence of a race between a copy-constructor and destructor /// implies a design flaw at a higher level. That race condition must be /// fixed at a higher design level, and no change to this class could fix it. //////////////////////////////////////////////////////////////////////////////// template