Thread: Smart pointer class

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654

    Smart pointer class

    Code:
    #pragma once
    #pragma warning ( disable: 4312 )
    
    class CMemoryManagerNull;
    template<typename T> class CMemoryManagerNew;
    
    // Requires MFC.
    // Requires afxmt.h
    
    template<typename T> class CMemoryManager
    {
    protected:
    #define MM_ERROR(x, y) try { x; } catch(char*) { y; }
    #define MM_ERROR_BLOCK(x) try { x; } catch(char*)
    	struct PointerInfo
    	{
    		DWORD dwRefCount;
    		CCriticalSection cSync;
    		bool bArray;
    		bool bSetIfArray;
    		bool bExistInMap;
    	};
    	struct PointerInfoTemp
    	{
    		PointerInfo* m_pTempInfo;
    		void* pForAddr;
    		bool bLookedUp;
    	};
    
    	T* p;
    	PointerInfo* m_pInfo;
    	//PointerInfoTemp m_PTempInfoLocal; // 
    	PointerInfoTemp m_PTempInfo;
    	DWORD m_dwThreadId;
    	bool m_bThreadSafe;
    	bool m_bTimeCritical;
    	
    	static CMap<const void*, const void*, PointerInfo*, PointerInfo*> m_InfoMap;
    	static CCriticalSection cThisSync;
    
    	void Init()
    	{
    		p = NULL;
    		m_pInfo = NULL;
    	}
    	void FirstInit(bool bThreadSafe = false, bool bTimeCritical = false)
    	{
    		m_bTimeCritical = bTimeCritical;
    		m_bThreadSafe = bThreadSafe;
    		m_dwThreadId = GetCurrentThreadId();
    		m_PTempInfo.m_pTempInfo = NULL;
    		m_PTempInfo.pForAddr = NULL;
    		m_PTempInfo.bLookedUp = false;
    	}
    	void CheckPointerInfo()
    	{
    		if (m_pInfo == NULL) 
    		{
    			m_pInfo = new PointerInfo;
    			m_pInfo->bArray = false;
    			m_pInfo->bSetIfArray = false;
    			m_pInfo->bExistInMap = false;
    			m_pInfo->dwRefCount = 0;
    		}
    	}
    	bool LookupSetRefCount(const T* p)
    	{
    		ASSERT(!m_bTimeCritical);
    		if ( !m_PTempInfo.bLookedUp || (m_PTempInfo.bLookedUp && m_PTempInfo.pForAddr != p) )
    		{
    			CMemoryManager::cThisSync.Lock();
    			CMemoryManager::m_InfoMap.Lookup(p, m_PTempInfo.m_pTempInfo);
    			CMemoryManager::cThisSync.Unlock();
    			if (m_PTempInfo.m_pTempInfo)
    			{
    				m_PTempInfo.pForAddr = (T*)p;
    				m_PTempInfo.bLookedUp = true;
    			}
    		}
    		if (m_PTempInfo.m_pTempInfo)
    		{
    			delete m_pInfo;
    			m_pInfo = m_PTempInfo.m_pTempInfo;
    			IncreaseRefCount();
    			return true;
    		}
    		return false;
    	}
    	void SetRefCount(DWORD dwNewCount) const
    	{
    		CheckPointerInfo();
    		if (!m_bTimeCritical) Verify();
    		Lock();
    		*m_pInfo->pdwRefCount = dwNewCount;
    		Unlock();
    	}
    	void IncreaseRefCount()
    	{
    		CheckPointerInfo();
    		if (!m_bTimeCritical) Verify();
    		Lock();
    		m_pInfo->dwRefCount++;
    		Unlock();
    	}
    	void TraceErrorAndDelete(CMemoryException* pException, CString strCallFunction, CString strErrFunction, const T* pMemory, bool bArray)
    	{
    		CString strError;
    		pException->GetErrorMessage(strError.GetBuffer(100000), 100000);
    		strError.ReleaseBuffer();
    		TRACE("&#37;s: Exception in %s:\n%s\nUnable to complete.", strCallFunction, strErrFunction, strError);
    		pException->Delete();
    		if (bArray)
    			delete [] pMemory;
    		else
    			delete pMemory;
    		ASSERT(FALSE);
    	}
    	void SetNewPointer(T* pNew, bool bArray)
    	{
    		if (p == pNew) return; // SPECIAL CASE: If we assign a pointer to the class that the class is already handling, the stop here. The chances are that if we proceed, it will decrement the reference 
    							   // count of the memory and delete it because it reaches 0. Thus, the memory we're trying to assign also gets freed! To avoid this, we do a return here.
    		InternalDelete();
    		if (!m_bTimeCritical) 
    		{
    			try
    			{
    				if ( LookupSetRefCount(pNew) )
    					ASSERT(m_pInfo->bArray == bArray); // Make sure the calle actually passed the right value for bArray; otherwise there's a programming error or the incorrect memory found!
    			}
    			catch(CMemoryException* pException)
    			{
    				TraceErrorAndDelete(pException, _T("CMemoryManager::SetNewPointer"), _T("CMemoryManager::LookupSetRefCount"), pNew, bArray);
    				return;
    			}
    		}
    		try
    		{
    			IncreaseRefCount();
    		}
    		catch(CMemoryException* pException)
    		{
    			TraceErrorAndDelete(pException, _T("CMemoryManager::SetNewPointer"), _T("CMemoryManager::IncreaseRefCount"), pNew, bArray);
    			return;
    		}
    		p = pNew;
    		m_pInfo->bArray = bArray;
    		if (!m_bTimeCritical) SetPointerInMap();
    		if (!m_bTimeCritical) Verify();
    	}
    	void InternalDelete()
    	{
    		InternalDetach();
    		Init();
    	}
    	void SetPointerInMap() const
    	{
    		ASSERT(!m_bTimeCritical);
    		if (!m_pInfo->bExistInMap)
    		{
    			CMemoryManager::cThisSync.Lock();
    			CMemoryManager::m_InfoMap.SetAt(p, m_pInfo);
    			CMemoryManager::cThisSync.Unlock();
    			m_pInfo->bExistInMap = true;
    		}
    	}
    	void DeletePointerInMap() const
    	{
    		ASSERT(!m_bTimeCritical);
    		if (m_pInfo->bExistInMap)
    		{
    			CMemoryManager::cThisSync.Lock();
    			CMemoryManager::m_InfoMap.RemoveKey(p);
    			CMemoryManager::cThisSync.Unlock();
    			m_pInfo->bExistInMap = false;
    		}
    	}
    	void UpdateMemoryManager(const CMemoryManager& rmm)
    	{
    		InternalDelete();
    		if (rmm.p == NULL) return;
    		p = rmm.p;
    		m_pInfo = rmm.m_pInfo;
    		IncreaseRefCount();
    		if (!m_bTimeCritical) Verify();
    	}
    	void SetNewAddr(T* pNewAddr, DWORD dwRefCount) const
    	{
    		Delete();
    		SetNewPointer(pNewAddr);
    		SetRefCount(dwRefCount);
    		if (!m_bTimeCritical) Verify();
    	}
    	void InternalDetach()
    	{
    		if (m_pInfo == NULL) return;
    		ASSERT(!m_pInfo->bSetIfArray); // This will assert if the class was unable to determine if an array was assigned and the caller hasn't called SetIfArray()
    		if (m_pInfo->dwRefCount - 1 <= 1) 
    		{
    			Lock();
    			if (!m_bTimeCritical) DeletePointerInMap();
    			if (m_pInfo->bArray)
    				delete [] p;
    			else
    				delete p;
    			delete m_pInfo;
    			Unlock();
    			Init();
    		}
    		else
    		{
    			Lock();
    			m_pInfo->dwRefCount--;
    			Unlock();
    		}
    	}
    	void Verify() const
    	{
    #ifdef _DEBUG
    		//ASSERT(p != NULL);
    		ASSERT(!m_bTimeCritical);
    		ASSERT(m_pInfo != NULL);
    #endif
    	}
    	bool IsMemoryArray(const T* p)
    	{
    		ASSERT(!m_bTimeCritical);
    		if ( !m_PTempInfo.bLookedUp || (m_PTempInfo.bLookedUp && m_PTempInfo.pForAddr != p) )
    		{
    			CMemoryManager::cThisSync.Lock();
    			CMemoryManager::m_InfoMap.Lookup(p, m_PTempInfo.m_pTempInfo);
    			CMemoryManager::cThisSync.Unlock();
    			m_PTempInfo.pForAddr = (T*)p;
    			m_PTempInfo.bLookedUp = true;
    		}
    		//CMemoryManager::m_InfoMap.Lookup(p, pInfo);
    		if (m_PTempInfo.m_pTempInfo)
    			return m_PTempInfo.m_pTempInfo->bArray;
    		else
    			throw "m_PTempInfo.m_pTempInfo == NULL";
    		return false;
    	}
    	void OperatorAssign(T* pmm)
    	{
    		bool bArray = false;
    		bool bSetIfArray = false;
    		bool bGuessIfArray = false;
    		if (!m_bTimeCritical)
    			MM_ERROR_BLOCK( bArray = IsMemoryArray(pmm) ) { bGuessIfArray = true; }
    		else
    		{
    			TRACE("\nCMemoryManager::void operator = (const T*& pmm):\nWARNING: Time Critical mode enabled. No check will me made to see if pointer is an array.\nAttempting to guess.");
    			bGuessIfArray = true;
    		}		
    		if (bGuessIfArray)
    		{
    			TRACE("\nCMemoryManager::void operator = (const T*& pmm):\nWARNING: Unable to determine if pointer is array or not. Attempting to guess.\n");
    			size_t nSize = _msize((void*)pmm);
    			if ( nSize > sizeof(*pmm) )
    			{
    				if (nSize % sizeof(*pmm) == 0) // Size of memory block is greater than sizeof the pointer and can be divided evenly. That probably means there are more than 1 element.
    					TRACE("NOTICE: Size of memory block pointed to by pmm was greater than the size of the data type, but evenly dividable by the data size. Pointer is assumed to be an array.\n");
    				else
    				{
    					TRACE("WARNING: Size of memory block pointed to by pmm was greater than the size of the data type, but was NOT evenly dividable by the data size. Pointer may or may not be an array. A call to SetIfArray() is required.\n");
    					bSetIfArray = true;
    				}
    				bArray = true;
    			}
    			else
    			{
    				TRACE("NOTICE: Size of memory block pointed to by pmm was equal to the size of the data type. No array was detected.\n");
    				bArray = false;
    			}
    			TRACE("\n");
    		}
    		SetNewPointer(pmm, bArray);
    		if (bSetIfArray) m_pInfo->bSetIfArray = true;
    		if (!m_bTimeCritical) Verify();
    	}
    
    public:
    	CMemoryManager()
    	{
    		FirstInit();
    		Init();
    	}
    	explicit CMemoryManager(bool bThreadSafe, bool bTimeCritical)
    	{
    		FirstInit(bThreadSafe, bTimeCritical);
    		Init();
    	}
    	CMemoryManager(int/* nNull*/)
    	{
    		FirstInit();
    		Init();
    	}
    	//CMemoryManager(const CMemoryManager<T>& rmm)
    	//{
    	//	FirstInit();
    	//	Init();
    	//	UpdateMemoryManager(rmm);
    	//}
    	//CMemoryManager(CMemoryManagerNull*& rNull)
    	//{
    	//	FirstInit();
    	//	delete rNull;
    	//	Init();
    	//}
    	//CMemoryManager(const T* p)
    	//{
    	//	bThreadSafe = true;
    	//	dwThreadId = GetCurrentThreadId();
    	//	Init();
    	//	OperatorAssign(p);
    	//}
    	~CMemoryManager()
    	{
    		InternalDetach();
    	}
    	void Lock() const
    	{
    #ifdef _DEBUG
    		ASSERT(GetCurrentThreadId() == m_dwThreadId); // The class should only be used by one thread only - and that's the thread that created it.
    #endif
    		if (p == NULL) return;
    		if (m_bThreadSafe) m_pInfo->cSync.Lock();
    	}
    	void Unlock() const
    	{
    		ASSERT(GetCurrentThreadId() == m_dwThreadId); // The class should only be used by one thread only - and that's the thread that created it.
    		if (p == NULL) return;
    		if (m_bThreadSafe) m_pInfo->cSync.Unlock();
    	}
    	void SetIfArray(bool bArray) const
    	{
    		ASSERT(bSetIfArray); // Should only be called if user is required to set if it's an array or not.
    		if (!bSetIfArray) return;
    		CheckPointerInfo();
    		m_pInfo->bArray = bArray;
    		bSetIfArray = false;
    	}
    	void Release()
    	{
    		InternalDelete();
    	}
    	// These functions are synonyms for Release, to be used to make code more readable.
    	void RemoveRef() const { Delete(); }
    	// Memory handled by this class is freed / allocated automatically, as therefore, it is not allowed to tamper with the memory pointer by setting it to NULL for example.
    	// The only allowed things to do are update the pointer and free the memory upon which it is set to NULL automatically.
    	void operator = (int Val) const { ASSERT(Val == NULL); Delete(); Verify(); }
    	bool operator == (int nCompare) const { return (p == (T*)nCompare); }
    	void operator = (T* pmm)
    	{
    		OperatorAssign(pmm);
    	}
    	//void operator = (T* pmm)
    	//{
    	//	*this = (const T*)pmm;
    	//}
    	void operator = (const CMemoryManager& rmm)
    	{
    		UpdateMemoryManager(rmm);
    		//Verify();
    	}
    	//void operator = (const CMemoryManagerNull* pNull)
    	//{
    	//	delete pNull;
    	//	Delete();
    	//}
    	//void AttachNewPointer(T* pNew)
    	//{
    	//	// If you have done something with the memory that renders the old pointer invalid (like reallocating), then use this function to set the DEBUG_NEW pointer.
    	//	// DO NOT USE OTHERWISE. This function does not perform any checks, modify the reference count, or anything else. If you need to assign a DEBUG_NEW pointer, use the assigment
    	//	// operator.
    	//	p = pNew;
    	//	Verify();
    	//}
    
    	void New(bool bArray = false, DWORD dwElements = 1)
    	{
    		InternalDelete();
    		T* pMemory;
    		if (bArray)
    			pMemory = new T[dwElements];
    		else
    			pMemory = new T;
    		SetNewPointer(pMemory, bArray);
    		if (!m_bTimeCritical) Verify();
    	}
    	void Attach(T*& pToAttach, bool bArray = false/*, DWORD dwRefCount = 1*/)
    	{
    		// If you got some memory you want the class to manage (example: data passed by thread you want the thread to clean up), then this is the function for you!
    		// PLEASE NOTE: Original pointer will be NULL after this function! Do not use or manipulate the memory once you've attached it to the class (hence why the original pointer will be NULL)!
    		// 
    		//SetNewPointer(pToAttach, bArray);
    		bArray; // bArray is kept for backwards compability
    		*this = pToAttach;
    		pToAttach = NULL;
    	}
    
    	template<typename T> static CMemoryManager<T> MemoryManagerNew(bool bArray, T* pNew)
    	{
    		return MemoryManagerNew(pNew, bArray);
    	}
    	template<typename T> static CMemoryManager<T> MemoryManagerNew(T* pNew, bool bArray)
    	{
    		CMemoryManager<T> mm;
    		T* pLocal = pNew;
    		mm.Attach(pLocal, bArray);
    		return mm;
    	}
    
    	// Used to cast or transform a memory manager of type TranformFrom to TranformTo. Use with caution. Primarily for use with polymorphism.
    	//template<typename TranformFrom, typename TranformTo> static CMemoryManager<TranformTo> MemoryManagerTansform(CMemoryManager<TranformFrom> pTransformFrom)
    	//{
    	//	TranformFrom* pTransform = (TranformFrom*)pTransformFrom;
    	//	TranformTo* pTransformTo = dynamic_cast<TranformTo>(pTransform); // Security check to make sure it's safe to transform from T1 to T2
    	//	CMemoryManager<TransformTo> pTransformToB;
    	//	pTransformToB.p = pTransformTo;
    	//	pTransformToB.pdwRefCount = pTransformFrom.pdwRefCount;
    	//	pTransformToB.IncreaseRefCount();
    	//	pTransformToB.bArray = pTransformFrom.bArray;
    	//	//pTransformToB.Attach(pTransformFrom.IsArray(), &pTransform, pTransformFrom.GetReferenceCount() + 1);
    	//	return pTransformTo;
    	//}
    
    	//void SetTimeCritical(bool bTimeCritical) { m_bTimeCritical = bTimeCritical; }
    	//void SetThreadSafe(bool bThreadSafe) { m_bThreadSafe = bThreadSafe; }
    	int GetSize() const { ASSERT(p); return sizeof(*p); }
    	size_t GetTotalSize() const { Lock(); size_t nSize = _msize(p); Unlock(); return nSize; }
    	int GetElements() const { return GetTotalSize() / sizeof(*p); }
    	void Resize(size_t dwNewSize) const { Lock(); realloc(p, dwNewSize); Unlock(); }
    	void ResizeElements(DWORD dwNewSize) const { Lock(); realloc(p, sizeof(*p) * dwNewSize); Unlock(); }
    	bool IsArray() const { return bArray; }
    	DWORD GetRefCount()  const
    	{ 
    		Lock();
    		DWORD dwToReturn = 0;
    		if (pdwRefCount) dwToReturn = *pdwRefCount;
    		Unlock();
    		return dwToReturn;
    	}
    
    	T& operator * () const { Lock(); T& r = *p; Unlock(); return r; }
    	T& operator * (CMemoryManagerNew<T>& Value) const { ASSERT(p); Lock(); *p = Value; Unlock(); }
    	T* operator -> () const { ASSERT(p); return p; }
    	operator T* () const { return p; }
    	operator void* () const { return p; }
    	//operator CMemoryManager<T>* () { return this; }
    	operator T& () const { return *p; }
    	T* GetPointer() const { return p; } 
    	bool operator == (T* pCompare) const { ASSERT(p); Lock(); bool b = (*p == *pCompare); Unlock(); return b; }
    	bool operator == (const CMemoryManager<T>& rCompare) const { ASSERT(p); Lock(); bool b = (*p == *rCompare.p); Unlock(); return b; }
    	bool operator == (const CMemoryManager<T>* pCompare) const { ASSERT(p); Lock(); bool b = (*p == pCompare->p); Unlock(); return b; }
    	bool operator == (const CMemoryManagerNull&) const { return (p == NULL); }
    	bool operator == (const CMemoryManagerNull* pNull) const { Lock(); delete pNull; return (p == NULL); Unlock(); }
    	//bool operator != (int nCompare) { return (p != (T*)nCompare); }
    	bool operator != (T* pCompare) const { ASSERT(p); Lock(); bool b = (*p != *pCompare); Unlock(); return b; }
    	bool operator != (const CMemoryManager<T>& rCompare) const { ASSERT(p); Lock(); bool b = (*p != *rCompare.p); Unlock(); return b; }
    	bool operator != (const CMemoryManager<T>* pCompare) const { ASSERT(p); Lock(); bool b = (*p != pCompare->p); Unlock(); return b; }
    	//void operator = (CMemoryManagerNull* pNull) { Delete(); Init(); Verify(); delete pNull; }
    	operator bool () const { return p != NULL; }
    	// Restrict operator +? They're dangerous!
    	//T* operator + (UINT64 add) explicit const { return p + add; }
    	//T* operator + (DWORD add) explicit const { return p + add; }
    	//T* operator + (T* add) explicit const { return p + add; }
    	template<typename NewT> operator CMemoryManager<NewT>& () const 
    	{
    		//T* pFrom = p;
    		NewT* pTo = dynamic_cast<NewT*>(p); // Security cast to make sure the new type in compatible with the old
    		ASSERT(pTo);
    		return *(CMemoryManager<NewT>*)this; 
    	}
    	T& operator [] (UINT index) const
    	{
    		Lock();
    		UINT nSize = _msize(p);
    		Unlock();
    		ASSERT(sizeof(p) * index > nSize);
    		T& rReturn = p[index]; 
    		return rReturn;
    	}
    	//T& operator [] (int index) { return p[index]; }
    	//T* P() { return p; }
    	void operator = (CMemoryManagerNull* p) const { delete p; Release(); }
    	//operator CMemoryManagerDefault<T> { return new CMemoryManagerDefault
    
    protected:
    	void operator delete(void* /*p*/) { }
    	void operator delete(void* /*p*/, size_t /*_Count*/) { }
    	//CMemoryManager<T>* operator & ();
    	void* operator new(size_t _Count) throw();
    	void* operator new(size_t _Count, const std::nothrow_t&) throw();
    	void* operator new(size_t _Count, void* _Ptr) throw();
    };
    
    template<typename T> class CMemoryManagerNew: public CMemoryManager<T>
    {
    public:
    	//CMemoryManager(bool bAllocate)
    	//{
    	//	Init();
    	//	if (bAllocate) New();
    	//}
    	CMemoryManagerNew()
    	{
    		//Init();
    		New();
    		Verify();
    	}
    	CMemoryManagerNew(bool bArray, T* pNew)
    	{
    		//Init();
    		SetNewPointer(pNew, bArray);
    		Verify();
    	}
    	CMemoryManagerNew(T NewValue)
    	{
    		//Init();
    		New();
    		Verify();
    		*p = NewValue;
    	}
    	CMemoryManagerNew(bool bThreadSafe, bool bTimeCritical)
    	{
    		FirstInit(bThreadSafe, bTimeCritical);
    		*(CMemoryManager*)this = rpp;
    	}
    	CMemoryManagerNew(bool bArray, T* pNew, bool bThreadSafe, bool bTimeCritical)
    	{
    		FirstInit(bThreadSafe, bTimeCritical);
    		SetNewPointer(pNew, bArray);
    		Verify();
    	}
    	CMemoryManagerNew(T NewValue, bool bThreadSafe, bool bTimeCritical)
    	{
    		FirstInit(bThreadSafe, bTimeCritical);
    		New();
    		Verify();
    		*p = NewValue;
    	}
    };
    
    template<typename T> class CMemoryManagerDefault: public CMemoryManager<T>
    {
    public:
    	CMemoryManagerDefault(CMemoryManagerNull* pNull)
    	{
    		delete pNull;
    		Release();
    	}
    	CMemoryManagerDefault(CMemoryManager<T>& rpp)
    	{
    		*(CMemoryManager*)this = rpp;
    	}
    };
    
    template<typename T> CMap<const void*, const void*, typename CMemoryManager<T>::PointerInfo*, typename CMemoryManager<T>::PointerInfo*> CMemoryManager<T>::m_InfoMap;
    template<typename T> CCriticalSection CMemoryManager<T>::cThisSync;
    
    class CMemoryManagerNull { };
    
    #pragma warning ( default: 4312 )
    #define pp CMemoryManager
    #define ppnew CMemoryManagerNew
    #define ppdef CMemoryManagerDefault
    #define newpp CMemoryManager<CHAR>::MemoryManagerNew
    //#define tpp CMemoryManager<CHAR>::MemoryManagerTansform
    //#define PPNULL new CMemoryManagerNull
    Last edited by Elysia; 10-31-2007 at 10:36 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Message class ** Need help befor 12am tonight**
    By TransformedBG in forum C++ Programming
    Replies: 1
    Last Post: 11-29-2006, 11:03 PM
  2. Direct3D problem
    By cboard_member in forum Game Programming
    Replies: 10
    Last Post: 04-09-2006, 03:36 AM
  3. My Window Class
    By Epo in forum Game Programming
    Replies: 2
    Last Post: 07-10-2005, 02:33 PM
  4. base class pointer problems
    By ... in forum C++ Programming
    Replies: 3
    Last Post: 11-16-2003, 11:27 PM
  5. Warnings, warnings, warnings?
    By spentdome in forum C Programming
    Replies: 25
    Last Post: 05-27-2002, 06:49 PM