/* ZSmartPointer.hpp Author: James Russell Created: 08/12/2011 Purpose: Smart pointer implementation for the ZEngine. Frees up the programmer from having to worry too hard about the minutiae of memory management for allocated objects. Not to use on arrays; use ZArray for dynamic array management. Uses atomic operations to enable the smart pointer to work across threads. Don't pass these things around by pointer or reference unless you really know what you are doing. You won't like the results. License: TODO */ #pragma once #ifndef _ZSMARTPOINTER_H #define _ZSMARTPOINTER_H #include #include #include #include #include #include #include #include #include //Shorthand used for ZSmartPointer #define ZPtr ZSmartPointer /* Default deallocator functor for ZSmartPointer, which is responsible for deallocating the managed object. This default version uses 'zdelete'. The template parameter T is the type of object pointed to by the smart pointer. The unary functor takes a single argument of type T*, which is the object to be deallocated. */ template struct ZSmartPointerDeallocator { void operator () (T* _object) { zdelete _object; } }; /* NULL deallocator functor for ZSmartPointer, which doesn't actually deallocate anything. The template parameter T is the type of object pointed to by the smart pointer. The unary functor takes a single argument of type T*, which is destroyed. */ template struct ZSmartPointerNullDeallocator { void operator() (T* _object) { URFP(_object); } }; /* Smart pointer class, used to share a references to an allocated object. This smart pointer uses atomic operations to ensure that the reference count is maintained properly even when the object is shared among threads. The template parameter T is the type of the object pointed to. The template parameter DF is the type of object deallocator functor to use. The template parameter R is the type of reference counter allocator to use. */ template , typename R = ZReferenceCounterAllocator > class ZSmartPointer { protected: //The pointed to object T* Object; //Object Deallocator DF ObjDeallocator; //Reference Counter Allocator R RefAllocator; //Reference Counter for the pointer (also contains the pointer) ZReferenceCounter* Ref; //Checks to see if we need a reference counter, allocates if so, and increments the strong //reference count inline void ReferenceGained() { if (Object == NULL) return; if (Ref == NULL) { Ref = RefAllocator.AllocateCounter(); ZASSERT_RUNTIME(Ref != NULL, "ZSmartPointer: Reference counter allocator returned NULL!"); } Ref->GainStrongRef(); } //Checks to see if we can delete the reference and lowers the count inline void ReferenceLost() { if (Ref == NULL) { ZASSERT_UTIL(Object == NULL, "ZSmartPointer: Reference lost with no reference counter, but object is still present!"); return; } ZASSERT_UTIL(Ref->GetStrongRefCount() > 0 && Object != NULL, "ZSmartPointer: Reference lost but object is already deallocated!"); uint32_t combinedCount = Ref->LoseStrongRef(); //Decrement our strong count and get the combined count //Check if we lost the last strong ref if ((combinedCount & ZREFCOUNTER_STRONG_MASK) == 0) //Note we don't use the EXTRACT_STRONG_REF() since 0 bitshifted is still 0 { Ref->SignalDeallocateObject(); //Signal object deallocation ObjDeallocator(Object); //Deallocate the object //Check if we lost the last weak reference if ((combinedCount & ZREFCOUNTER_WEAK_MASK) == 0) //Note we don't use the EXTRACT_WEAK_REF() since 0 bitshifted is still 0 { RefAllocator.DeallocateCounter(Ref); //Deallocate the counter } } //We lost our reference, so we don't need these Object = NULL; Ref = NULL; } public: /* Default constructor. Sets the reference to NULL and does not allocate a reference counter. */ ZSmartPointer() : Object(NULL), Ref(NULL) { } /* Constructor. Explicit to ensure we don't accidentally take ownership of a pointer. Allocates a reference counter from the reference counter allocator. @param _ptr - pointer to be managed */ explicit ZSmartPointer(T* _ptr) : Object(_ptr), Ref(NULL) { ReferenceGained(); } /* Copy constructor. @param _other - the other smart pointer */ ZSmartPointer(const ZSmartPointer& _other) : Object(_other.Pointer()), Ref(_other.Counter()) { ZASSERT_UTIL(!(Object != NULL && Ref == NULL), "ZSmartPointer: Copy constructor called from smart pointer with invalid reference counter!"); ReferenceGained(); } /* Conversion constructor. Used to auto up-cast derived classes or other classes that can be converted. @param D - the typename of the derived class @param EF - the deallocator functor type of the other class @param S - the reference counter allocator type of the other class @param _other - the other smart pointer */ template ZSmartPointer(const ZSmartPointer& _other) : Object(static_cast(_other.Pointer())), Ref(_other.Counter()) { ZASSERT_UTIL(!(Object != NULL && Ref == NULL), "ZSmartPointer: Copy constructor called from smart pointer with invalid reference counter!"); ReferenceGained(); } /* Destructor. Causes a strong reference to be lost. */ ~ZSmartPointer() { ReferenceLost(); } /* = operator overload. Sets this smart pointer to be the owner of the provided pointer, which may require allocation of a reference counter. @param _ptr - the pointer to the object to own @return (ZSmartPointer&) - this smart pointer */ ZSmartPointer& operator = (T* _ptr) { if (Object == _ptr) return *this; if (_ptr == NULL) { ReferenceLost(); //Ditch our reference Object = NULL; //Nullify our object pointer Ref = NULL; //Nullify our counter } else { if (Object != NULL) ReferenceLost(); //Lose our reference to the valid object Object = _ptr; //Own it Ref = NULL; //Make sure Ref is set to NULL ReferenceGained(); //Create a new reference counter } return *this; } /* = operator overload. Sets this smart pointer to provide a strong reference to the same object managed by the other smart pointer. @param _other - the other smart pointer @return (ZSmartPointer&) - this smart pointer */ ZSmartPointer& operator = (const ZSmartPointer& _other) { if (*this == _other || Ref == _other.Counter()) return *this; ReferenceLost(); //Lose a reference to our current object Object = _other.Pointer(); //Own the new one Ref = _other.Counter(); //Get our new reference counter ReferenceGained(); //Increment the reference return *this; } /* = operator overload. Used to assign to derived classes and classes that can be converted to T. @param D - the type held by the other smart pointer @param EF - the deallocator type of the other smart pointer @param S - the reference counter allocator type of the other smart pointer @param _other - the other smart pointer @return (ZSmartPointer&) - this smart pointer */ template ZSmartPointer& operator = (const ZSmartPointer& _other) { T* testCast = static_cast(_other.Pointer()); ((void)testCast); //This will fail to compile if T* cannot static cast to D* return *this = static_cast< ZSmartPointer >(_other); } /* == operator overload. Checks to see if this smart pointer owns the provided raw pointer. @param _ptr - the pointer to check @return (bool) - true if the pointer is managed by this smart pointer, false otherwise */ bool operator == (const T* _ptr) const { return Object == _ptr; } /* == operator overload. Checks to see if these pointers reference the same object. @param _other - the other smart pointer @return (bool) - true if equal, false otherwise */ bool operator == (const ZSmartPointer& _other) const { return Object == _other.Object; } /* == operator overload. Used to compare with smart pointers referencing derived classes or a class that can be converted to T. @param _other - the other smart pointer @return (bool) - true if equal, false otherwise */ template bool operator == (const ZSmartPointer& _other) const { return Object == static_cast( _other.Pointer()); } /* != operator overload. Checks to see if this smart pointer does not own the provided raw pointer. @param _ptr - the pointer to check @return (bool) - true if this smart pointer does not reference the provided, false otherwise */ bool operator != (const T* _ptr) const { return !(*this == _ptr); } /* != operator overload. Checks to see if the smart pointers reference different objects. @param _other - the other smart pointer @return (bool) - true if this smart pointer and the other do not reference the same object, false otherwise */ bool operator != (const ZSmartPointer& _other) const { return !(*this == _other); } /* != operator overload. Checks to see if these pointers reference different objects. Used to compare with derived classes. @param D - the type contained by the other smart pointer @param EF - the allocator used by the other smart pointer @param S - the reference counter allocator type used by the other smart pointer @param _other - the other smart pointer @return (bool) - true if this smart pointer and the other do not reference the same object, false otherwise */ template bool operator != (const ZSmartPointer& _other) const { return !(*this == _other); } /* -> operator overload. Allows this to be accessed as a T* would be. @return (T*) - raw pointer to the object @assert - if the pointer is NULL */ T* operator -> () const { ZASSERT_RUNTIME(Object != NULL, "ZSmartPointer: Null Pointer Dereferenced!"); return Object; } /* '*' operator overload. Allows this to be dereferenced as a T* would be. @return (T&) - reference to the object @assert - if the object is NULL */ T& operator * () const { ZASSERT_RUNTIME(Object != NULL, "ZSmartPointer: Null Pointer Dereferenced!"); return *Object; } /* public ZSmartPointer::CounterAllocator Gets the reference counter allocator for this smart pointer. @return (R&) - the reference counter allocator */ R& CounterAllocator() const { return RefAllocator; } /* public ZSmartPointer::Cast Used to cast this smart pointer into another type. This will construct an additional smart pointer that also references the object. The owned object must be convertible to type C* via static_cast. @param C - the type contained by the other type @return (ZSmartPointer) - this smart pointer as another type */ template ZSmartPointer Cast() { return static_cast< ZSmartPointer >(*this); } /* public ZSmartPointer::Counter Gets the ZRefrenceCounter instance used by this smart pointer. @return (ZReferenceCounter*)- ZRefCounter instance */ ZReferenceCounter* Counter() const { return Ref; } /* public ZSmartPointer::Deallocator Gets the object deallocator for this smart pointer. @return (DF&) - reference to the object deallocator */ DF& Deallocator() const { return ObjDeallocator; } /* public ZSmartPointer::Detach Detaches the pointer from memory management by the smart pointer. Should only be called if there is a single strong reference to the object, otherwise it will return NULL. Weak references will be nullified as if the object has been deallocated. @return (T*) - raw pointer detached from this smart pointer, or NULL if more than one reference and unable to detach */ T* Detach() { if (Object == NULL || Ref == NULL || Ref->GetStrongRefCount() != 1) return NULL; else { uint32_t combinedCount = Ref->LoseStrongRef(); //Notify everyone we are losing this strong reference Ref->SignalDeallocateObject(); //Signal that we are deallocating the object if ((combinedCount & ZREFCOUNTER_WEAK_MASK) == 0) RefAllocator.DeallocateCounter(Ref); //Deallocate our reference counter T* ptr; ptr = Object; //Detach the pointer Object = NULL; //Nullify our object reference Ref = NULL; //Nullify our reference counter return ptr; } } /* public ZSmartPointer::Pointer Gets the smart pointer as a raw pointer. Buyer beware. @return (T*) - raw pointer that this smart pointer manages */ T* Pointer() const { return Object; } }; //////////////////////////////////////// /* ZArray Containment Specializations */ //////////////////////////////////////// //Helper function used to nullify references in a range template void ZSmartPointerArrayNullify(ZSmartPointer* _array, size_t _start, size_t _end) { for (size_t i = _start; i < _end; i++) _array[i] = NULL; } //Specialization for ZArray_ClearImpl containing ZSmartPointer template struct ZArray_ClearImpl< ZSmartPointer, A> { inline static void Call(ZArray, A>& _self) { //Nullify references ZSmartPointerArrayNullify(_self.Array, 0, _self.ArraySize); //Reset size and check integrity _self.ArraySize = 0; _self.CheckIntegrity(); } inline static void Call(ZArray, A>& _self, size_t _newCapacity) { //Nullify references ZSmartPointerArrayNullify(_self.Array, 0, _self.ArraySize); #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_newCapacity > 0, "ZArray: Cannot set capacity to zero!"); #endif //Reallocate if needed, drop our size to zero, and check integrity if (_newCapacity > _self.ArrayCapacity) { _self.Deallocate(_self.Array, _self.ArrayCapacity); _self.ArrayCapacity = _newCapacity; _self.Allocate(_self.ArrayCapacity); } _self.ArraySize = 0; _self.CheckIntegrity(); } }; //Specialization for ZArray_EraseImpl containing ZSmartPointer template struct ZArray_EraseImpl< ZSmartPointer, A> { inline static ZSmartPointer Call(ZArray, A>& _self, size_t _index) { size_t index = _self.BoundsCheck(_index, _self.ArraySize); //Grab the currently held element, shift the array down, reduce size, and check integrity ZSmartPointer element = _self.Array[index]; for (size_t i = index; i + 1 < _self.ArraySize; i++) _self.Array[i] = _self.Array[i + 1]; _self.ArraySize--; _self.CheckIntegrity(); //Nullify reference ZSmartPointerArrayNullify(_self.Array, _self.ArraySize, _self.ArraySize + 1); return element; } inline static void Call(ZArray, A>& _self, const size_t _start, const size_t _end) { size_t start = _self.BoundsCheck(_start, _self.ArraySize); size_t end = _self.BoundsCheck(_end, _self.ArraySize + 1); #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(start <= end, "ZArray: cannot erase with start < end!"); #endif //Copy the elements down, compute new size, and check integrity for (size_t idx = start; idx + (end - start) < _self.ArraySize; idx++) _self.Array[idx] = _self.Array[idx + (end - start)]; _self.ArraySize = _self.ArraySize - (end - start); _self.CheckIntegrity(); //Nullify references (where we moved from) ZSmartPointerArrayNullify(_self.Array, _self.ArraySize, _self.ArraySize + (end - start)); } }; //Specialization for ZArray_PopBackImpl containing ZSmartPointer template struct ZArray_PopBackImpl< ZSmartPointer, A> { inline static ZSmartPointer Call(ZArray, A>& _self) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!"); #endif ZSmartPointer ptr = _self.Array[--(_self.ArraySize)]; //Nullify reference ZSmartPointerArrayNullify(_self.Array, _self.ArraySize, _self.ArraySize + 1); //Grab the last element in the array and decrease our array size return ptr; } }; //Specialization for ZArray_PopFrontImpl containing ZSmartPointer template struct ZArray_PopFrontImpl< ZSmartPointer, A> { inline static ZSmartPointer Call(ZArray, A>& _self) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(_self.ArraySize > 0, "ZArray: Cannot pop from array with no elements!"); #endif //This will nullify the reference return _self.Erase(0); } }; //Specialization for ZArray_ResizeImpl containing ZSmartPointer template struct ZArray_ResizeImpl< ZSmartPointer, A> { inline static void Call(ZArray, A>& _self, const size_t _size) { //Nullify references if needed if (_size < _self.ArraySize) ZSmartPointerArrayNullify(_self.Array, _size, _self.ArraySize); //Check to see if we need more space, change our size, and check integrity if (_size > _self.ArrayCapacity) _self.Reserve(_size); _self.ArraySize = _size; _self.CheckIntegrity(); } inline static void Call(ZArray, A>& _self, const size_t _size, const ZSmartPointer& _value) { //Nullify references if needed if (_size < _self.ArraySize) ZSmartPointerArrayNullify(_self.Array, _size, _self.ArraySize); //See if we need more space, copy in the new value, change our size, and check integrity if (_size > _self.ArrayCapacity) _self.Reserve(_size); for (size_t i = _self.ArraySize; i < _size; i++) _self.Array[i] = _value; _self.ArraySize = _size; _self.CheckIntegrity(); } }; /////////////////////////////////////// /* ZList Containment Specializations */ /////////////////////////////////////// //Specialization for ZList_ClearImpl containing ZSmartPointer template struct ZList_ClearImpl < ZSmartPointer, A> { inline static void Call(ZList, A>& _self, ZListIterator< ZSmartPointer >& _itr) { ZListNode >* next; ZListNode >* current = _itr.GetNode(); _self.CheckIntegrity(); //Early out if (_self.Empty()) return; //If we are the starting node, be sure to reset the EmptyNode.Next pointer if (current == _self.EmptyNode.Next) _self.EmptyNode.Next = &_self.EmptyNode; //This is always true _self.EmptyNode.Previous = current->Previous; current->Previous->Next = &_self.EmptyNode; //Iterate and deallocate the nodes while (current != &_self.EmptyNode) { next = current->Next; current->Element = NULL; //This is the specialization _self.DeallocateNode(current); current = next; } //Set the node on the iterator equal to the end node _itr.SetNode(&_self.EmptyNode); _self.CheckIntegrity(); } }; //Specialization for ZList_EraseImpl containing ZSmartPointer template struct ZList_EraseImpl < ZSmartPointer, A> { inline static ZSmartPointer Call(ZList, A>& _self, ZListIterator< ZSmartPointer >& _itr) { ZSmartPointer elem; ZListNode< ZSmartPointer > *node = _itr.GetNode(); #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(node != NULL, "ZList: Iterator is invalid!"); ZSTL_ASSERT(node != &_self.EmptyNode, "ZList: Cannot erase end node!"); #endif //Increment the iterator to the next list node to keep the iterator valid ++_itr; //Rearrange the pointers node->Previous->Next = node->Next; node->Next->Previous = node->Previous; elem = node->Element; node->Element = NULL; //This is the specialization _self.DeallocateNode(node); _self.CheckIntegrity(); return elem; } inline static void Call(ZList, A>& _self, ZListIterator< ZSmartPointer >& _start, const ZListIterator< ZSmartPointer >& _end) { ZListNode< ZSmartPointer > *nodeStart = _start.GetNode(); ZListNode< ZSmartPointer > *nodeEnd = _end.GetNode(); ZListNode< ZSmartPointer > *curNode; ZListNode< ZSmartPointer > *prevNode; #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(nodeStart != NULL && nodeEnd != NULL, "ZList: Cannot erase with invalid iterator!"); ZSTL_ASSERT(nodeStart != &_self.EmptyNode, "ZList: Cannot erase end node!"); #endif //Rearrange the pointers nodeStart->Previous->Next = nodeEnd; nodeEnd->Previous = nodeStart->Previous; //Erase each element between from and to curNode = nodeStart; prevNode = NULL; while (curNode != nodeEnd) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(curNode != &_self.EmptyNode, "ZList: Cannot erase end node!"); #endif prevNode = curNode; curNode = curNode->Next; prevNode->Element = NULL; //This is the specialization _self.DeallocateNode(prevNode); } _start = _end; } }; //Specialization for ZList_PopBackImpl containing ZSmartPointer template struct ZList_PopBackImpl < ZSmartPointer, A> { inline static ZSmartPointer Call(ZList, A>& _self) { ZSmartPointer elem; ZListNode< ZSmartPointer > *node; #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(!_self.Empty(), "ZList: Cannot pop from back of empty list!"); #endif //Grab the element at the back of the list node = _self.EmptyNode.Previous; //Remove the node from the list node->Previous->Next = &_self.EmptyNode; _self.EmptyNode.Previous = node->Previous; //Get the element value and lose our reference elem = node->Element; node->Element = NULL; //Deallocate and then return _self.DeallocateNode(node); _self.CheckIntegrity(); return elem; } }; //Specialization for ZList_PopFrontImpl containing ZSmartPointer template struct ZList_PopFrontImpl < ZSmartPointer, A> { inline static ZSmartPointer Call(ZList, A>& _self) { ZSmartPointer elem; ZListNode< ZSmartPointer > *node; #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT(!_self.Empty(), "ZList: Cannot pop from front of empty list!"); #endif //Grab the element at the front of the list node = _self.EmptyNode.Next; //Remove the node from the list node->Next->Previous = node->Previous; node->Previous->Next = node->Next; //Get the element value and lose our reference elem = node->Element; node->Element = NULL; //Deallocate and then return _self.DeallocateNode(node); _self.CheckIntegrity(); return elem; } }; ///////////////////////////////////////////// /* ZRingBuffer Containment Specializations */ ///////////////////////////////////////////// //Specialization for ZRingBuffer_ClearImpl containing ZSmartPointer template struct ZRingBuffer_ClearImpl < ZSmartPointer, P, A> { inline static void Call( ZRingBuffer< ZSmartPointer, P, A>& _self ) { // set to null to remove references for (size_t i = 0; i < _self.BufferSize; i++) _self.At(i) = ZSmartPointer(NULL); _self.BufferSize = 0; _self.FrontIndex = 0; _self.BackIndex = 0; } inline static void Call( ZRingBuffer< ZSmartPointer, P, A>& _self, size_t _newCapacity ) { // set to null to remove references for (size_t i = 0; i < _self.BufferSize; i++) _self.At(i) = ZSmartPointer(NULL); _self.Buffer.Resize(_newCapacity); _self.BufferSize = 0; _self.FrontIndex = 0; _self.BackIndex = 0; } }; //Specialization for ZRingBuffer_EraseImpl containing ZSmartPointer template struct ZRingBuffer_EraseImpl < ZSmartPointer, P, A> { inline static T Call( ZRingBuffer< ZSmartPointer, P, A>& _self, size_t _index) { //We are going to do this in a very naive fashion, as this container is not intended for these //kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome. _self.AlignBuffer(); _self.BufferSize--; _self.BackIndex--; // set to null to remove references ZSmartPointer ret = _self.Buffer.At(_index); _self.Buffer.At(_index) = ZSmartPointer(NULL); return ret; } inline static void Call( ZRingBuffer< ZSmartPointer, P, A>& _self, size_t _i, size_t _j ) { //We are going to do this in a very naive fashion, as this container is not intended for these //kinds of operations anyhow. Anyone who wishes to optimize this at a later date is welcome. size_t count; _self.AlignBuffer(); count = (size_t)(_j - _i); _self.BufferSize = _self.BufferSize - count; _self.BackIndex = _self.BackIndex - count; // set to null to remove references for( size_t i = _i; i < _j; i++) _self.Buffer.At(i) = ZSmartPointer(NULL); _self.Buffer.Erase(_i, _j); } }; //Specialization for ZRingBuffer_PopBackImpl containing ZSmartPointer template struct ZRingBuffer_PopBackImpl < ZSmartPointer, P, A> { inline static T Call( ZRingBuffer< ZSmartPointer, P, A>& _self ) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::PopBack() caused underflow!"); #endif size_t index = _self.BackIndex; _self.DecrementBack(); _self.CheckIntegrity(); // set to NULL to fix ref count issues ZSmartPointer< T, DF, R > ret = _self.Buffer.At(index); _self.Buffer.At(index) = ZSmartPointer< T, DF, R>(NULL); return ret; } }; //Specialization for ZRingBuffer_PopFrontImpl containing ZSmartPointer template struct ZRingBuffer_PopFrontImpl < ZSmartPointer, P, A> { inline static T Call( ZRingBuffer< ZSmartPointer, P, A>& _self ) { #if !ZSTL_DISABLE_RUNTIME_CHECKS ZSTL_ASSERT( _self.BufferSize > 0, "ZRingBuffer::PopFront() caused underflow!"); #endif size_t index = _self.FrontIndex; _self.IncrementFront(); _self.CheckIntegrity(); // set to NULL to fix ref count issues ZSmartPointer< T, DF, R > ret = _self.Buffer.At(index); _self.Buffer.At(index) = ZSmartPointer< T, DF, R>(NULL); return ret; } }; #endif