// Copyright (C) 2000 Sean Cavanaugh // This file is licensed under the terms of the Lesser GNU Public License // (see LPGL.txt, or http://www.gnu.org/copyleft/lesser.txt) #if !defined(AFX_REFERENCEOBJECT_H__BAEBCE9D_CD68_40AF_8A54_B23A0D14E807__INCLUDED_) #define AFX_REFERENCEOBJECT_H__BAEBCE9D_CD68_40AF_8A54_B23A0D14E807__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" #endif #include "assert.h" #include "limits.h" #include "ReferenceCounter.h" template class ReferenceObjectBlock { public: DATA_T* pData; // User defined data block mutable ReferenceCounter ReferenceCount; unsigned int AllocLength; // Current size of array, in T sized units. if AllocLength = 0, pData is const and needs CopyOnWrite HEADER_T Header; // User defined header block (must be safe to copy with operator=) }; /*! \author Sean Cavanaugh \email sean@dimensionalrift.com \cvsauthor $Author: sean $ \date $Date: 2000/09/11 20:28:24 $ \version $Revision: 1.1 $ \brief ReferenceObject is a template class designed to be used a base class for pass-by-reference objects, to that things such as returning objects up the stack efficiently are possible. \bug EVERY non const function needs its very first operation to be CopyForWrite() \bug EVERY derived class should define its operator=() to call the ReferenceObject base class version (notice this operator is protected,) \bug HEADER_T will probably need an operator=() defined for all but the most trivial types \bug Store objects or simple data types, NO POINTERS (use ReferencePtrObject for that) (they will leak like mad) */ template class ReferenceObject { public: ReferenceObject(); ReferenceObject(unsigned int size); ReferenceObject(const ReferenceObject& other); virtual ~ReferenceObject(); public: HEADER_T& getHeader(); DATA_T* getData(); ReferenceObject& operator=(const ReferenceObject& other); protected: void AllocBlock(unsigned int size); // Allocate Header+Data Block (size in T units) void AllocHeader(); // Allocate Header Block void AllocData(unsigned int size); // Alocate Data Block (size in T units) virtual void Release(); // Releases a reference count (possibly freeing memory) virtual void InitHeader(); // User defined Header Initialization function virtual void FreeHeader(); // User defined Header destructor function virtual void CopyForWrite(); // Make unique copy for writing, must first instruction in all non const-functions in derived classes virtual void CopyForWrite(unsigned int size); // same as CopyForWrite() except takes a resize parameter ReferenceObjectBlock* m_pData; }; template ReferenceObject::ReferenceObject() { m_pData = NULL; AllocHeader(); InitHeader(); } template ReferenceObject::ReferenceObject(unsigned int size) { m_pData = NULL; AllocBlock(size); } template ReferenceObject::ReferenceObject(const ReferenceObject& other) { m_pData = other.m_pData; m_pData->ReferenceCount++; } template ReferenceObject::~ReferenceObject() { Release(); } template ReferenceObject& ReferenceObject::operator=(const ReferenceObject& other) { if (m_pData != other.m_pData) { Release(); m_pData = other.m_pData; m_pData->ReferenceCount++; } return *this; } template void ReferenceObject::Release() { assert(m_pData != NULL); if (m_pData->ReferenceCount.dec() <= 0) { FreeHeader(); delete[] m_pData->pData; delete m_pData; m_pData = NULL; } } template void ReferenceObject::AllocBlock(unsigned int size) { AllocHeader(); AllocData(size); InitHeader(); // Initialize user defined header } template void ReferenceObject::AllocHeader() { m_pData = new ReferenceObjectBlock; m_pData->ReferenceCount = 1; m_pData->pData = NULL; m_pData->AllocLength = 0; } template void ReferenceObject::AllocData(unsigned int size) { assert(m_pData != NULL); assert((size * sizeof(DATA_T)) < INT_MAX); if (size) { m_pData->pData = new DATA_T[size]; } else { m_pData->pData = NULL; } m_pData->AllocLength = size; } template void ReferenceObject::InitHeader() { // NOTE: Derive this function to initialize the Header object(s) } template void ReferenceObject::FreeHeader() { // NOTE: Derive this function to clean up the Header object(s) } template void ReferenceObject::CopyForWrite() { CopyForWrite(m_pData->AllocLength); } template void ReferenceObject::CopyForWrite(unsigned int size) { unsigned int oldsize = m_pData->AllocLength; if (size) { if ((m_pData->ReferenceCount > 1) || (size != oldsize)) { ReferenceObjectBlock* pTmp = m_pData; AllocBlock(size); m_pData->Header = pTmp->Header; unsigned int sizetocopy = min(size,oldsize); // memcpy(m_pData->pData, pTmp->pData, min(size,oldsize) * sizeof(DATA_T)); // memcpy not safe for objects for (unsigned int x=0;xpData[x] = pTmp->pData[x]; } if (pTmp->ReferenceCount.dec() <= 0) { delete[] pTmp->pData; delete pTmp; } } } else // Replace reference to a null object (since size is zero) { Release(); AllocHeader(); InitHeader(); } } template HEADER_T& ReferenceObject::getHeader() { CopyForWrite(); return m_pData->Header; } template DATA_T* ReferenceObject::getData() { CopyForWrite(); return m_pData->pData; } #endif // !defined(AFX_REFERENCEOBJECT_H__BAEBCE9D_CD68_40AF_8A54_B23A0D14E807__INCLUDED_)