Click here to Skip to main content
15,880,543 members
Articles / Desktop Programming / WTL

XONOR pointers: eXclusive Ownership & Non Owning Reference pointers

Rate me:
Please Sign up or sign in to vote.
4.96/5 (46 votes)
14 Apr 2014CPOL41 min read 125.8K   691   88  
A smart pointer system for safe application development in C++.
#pragma once

class critical_section :
	public CRITICAL_SECTION
{
public:
	critical_section()
	{
		::InitializeCriticalSection( this );
	}

	~critical_section()
	{
		::DeleteCriticalSection( this );
	}

	// Acquire the critical section
	void Enter()
	{
		::EnterCriticalSection( this );
	}
	// Release the critical section
	void Leave()
	{
		::LeaveCriticalSection( this );
	}

};

/*****************************************************************************************
Turn on monitoring in debug mode 
******************************************************************************************/
#ifdef _DEBUG
	#define MONITOR_REF_CONTROLERS
	#define MONITOR_PTRS
	#define VERIFY_INTEGRITY
#endif
#ifdef MONITOR_PTRS
	#define MONITOR_REF_CONTROLERS
#endif

/*****************************************************************************************
By default this system protects against class slicing and it is not necessary that 
base class destructors are declared virtual. xvirtual is defined as nothing. This protection 
incurs an extra overhead of two pointer values per reference controler.
If you are confident that you always use virtual destructors in polymorphic base classes then
place the following just above #include <ZonorPtrs.h> in your project

	#define  I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES

This will switch off the protection against class slicing and will also define xvirtual as
virtual. 

Use xvirtual to qualify polymorphic base class destructors so that they are only declared
virtual when I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES is defined.

******************************************************************************************/
#ifdef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
	#define xvirtual virtual
#else
	#define xvirtual
#endif


namespace XonorPtrs {

/*****************************************************************************************
								Forward declarations  
******************************************************************************************/

template <class T, bool t_bDelete=true> class owner_ptr;//Scoped pointer that owns and deletes the object 
template <class T> class owner_ptr_base;
template <class T> class ref_pointer_base;
template <class T> class new_ptr;

template <class T> class ref_ptr;//A general purpose weak reference, suitable for class members 
template <class T> class fast_ptr;//A full speed pointer to use within a code block

template <class T> class enable_ref_ptr_to_this;//An add-in templated base class which allows class to ref_ptr and fast_ptr pointers to this

template <class T> class strong_ptr;//A classic strong or shared pointer.


/*************************************************************************************************
class xonor_ptr_exception - recoverable exception class - to be thrown when no other defined 
behavior is possible. Typically these are throw when trying to dereference a NULL pointer
where throwing an exception is the only possible action. Although it is not a good practice
you can handle these execptions and continue execution. Generally they indicate that you have ommited
a test for NULL which you should correct.
***************************************************************************************************/
class xonor_ptr_exception
{
	
	CString m_csReason;
public:
	xonor_ptr_exception(CString csReason) 
	{
		m_csReason=csReason;
	};
   ~xonor_ptr_exception() {};

   const TCHAR *ShowReason() const 
   { 
	  return m_csReason; 
	}

};

/*************************************************************************************************
class xonor_ptr_fatal_exception - non-recoverable exception class - to be thrown when no 
continuation of execution is a bad idea.

IMPORTANT - This is a stop the program' exception you should NEVER handle it and continue execution.

Some of these exceptions are thrown during construction and destruction and therefore any attempt to
continue execution is going to be messy. This exception throwing pattern is NOT designed for run-time 
recovery. If you are getting these exceptions you have a serious coding error and you must correct it.

***************************************************************************************************/
class xonor_ptr_fatal_exception
{
	
	CString m_csReason;
public:
	xonor_ptr_fatal_exception(CString csReason) 
	{
		m_csReason=csReason;
	};
   ~xonor_ptr_fatal_exception() {};

   const TCHAR *ShowReason() const 
   { 
	  return CString(_T("FATAL exception"))+m_csReason; 
	}

};


/*****************************************************************************************
		T* down_cast(U* pU) a discriminating downcaster of raw pointers.
******************************************************************************************/
template<class T, class U> 
inline T* down_cast(U* pU)
{
	if(sizeof(T)>sizeof(U))
		throw xonor_ptr_fatal_exception(_T("Not a downcast"));
	return static_cast<T*>(pU);
}

namespace PRIVATE{

	template <class T> class reference_controler_impl;

	const int BlockSize=100;
	const int NumBlocks=100;
	
	class reference_controler
	{
		
		//*************************************************************************************************
		//Holds pointer and ref_ptr counts - created on the heap when a first 
		//reference is taken and self destructs when no references remain
		//************************************************************************************************
	public:
#ifdef MONITOR_PTRS
		static void SetMonitorOutput(CString& csString)
		{
			int Tot1=stm_owner_ptr_count*2+stm_ref_ptr_count*2+stm_strong_ptr_count*2+stm_reference_controller_count*2;
			int Tot2=stm_owner_ptr_count*2+stm_ref_ptr_count*2+stm_strong_ptr_count*2+stm_reference_controller_count*4;

			int Tot=stm_owner_ptr_count+stm_ref_ptr_count+stm_strong_ptr_count;

			int PerCent1=100*Tot1/Tot;
			int PerCent2=100*Tot2/Tot;

  #ifdef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
			csString.Format(_T("owner=%4d  ref=%4d  strong=%4d  r.conts.=%4d    total xptrs=%4d    xptr mem=%4d ptrs   %%ratio=%4d"), 
				stm_owner_ptr_count, stm_ref_ptr_count, stm_strong_ptr_count,
				stm_reference_controller_count, stm_owner_ptr_count+stm_ref_ptr_count+stm_strong_ptr_count, 
				 
				Tot1, 
				PerCent1 );
			
  #else
			
			csString.Format(_T("owner=%4d  ref=%4d  strong=%4d r.conts.=%4d    total xptrs=%4d    xptr mem=%4d ptrs   %%ratio=%4d"), 
				stm_owner_ptr_count, stm_ref_ptr_count, stm_strong_ptr_count, 
				stm_reference_controller_count, stm_owner_ptr_count+stm_ref_ptr_count+stm_strong_ptr_count, 
				 
				Tot2, 
				PerCent2);
  #endif
		}
#endif
		
		template <class memory_slot>
		class reference_controler_pool_base
		{
		protected:
			int m_CurrentBlockSize;
			class critical_section_lock
			{
				critical_section* m_pCriticalSection;
public:
				critical_section_lock(critical_section* pCriticalSection)
				{
					m_pCriticalSection=pCriticalSection;
					m_pCriticalSection->Enter();
				}
				~critical_section_lock()
				{
					m_pCriticalSection->Leave();
				}
			};
			
			class memory_block
			{
			public:
				int m_BlockSize;
				memory_slot* m_pMemorySlots;				
				memory_slot* m_pFirstFreeSlot;
				memory_block(int ThisBlockSize)
				{
					m_BlockSize=ThisBlockSize;
					m_pMemorySlots=new memory_slot[m_BlockSize];
					m_pFirstFreeSlot=m_pMemorySlots;
					(m_pMemorySlots+m_BlockSize-1)->m_Next=0;
				}
				~memory_block()
				{
					delete [] m_pMemorySlots;
				}
				memory_slot* give_free_slot()
				{
					if(NULL==m_pFirstFreeSlot)
						return NULL;
					memory_slot* p=m_pFirstFreeSlot;
					if(p->m_Next!=0)
						m_pFirstFreeSlot=p+p->m_Next;
					else
						m_pFirstFreeSlot=NULL;
					return p;
				}
				void recover_free_slot(memory_slot* p)
				{
					if(m_pFirstFreeSlot!=NULL)
					{
						int offset=m_pFirstFreeSlot-p;
						if(offset>m_BlockSize || offset<-m_BlockSize)
						{
							int BreakHere=0;
						}
						p->m_Next=offset;
					}
					else
						p->m_Next=0;
					m_pFirstFreeSlot=p;
				}
			};

			critical_section m_CriticalSection;
			
			memory_block* MemoryBlocks[NumBlocks];
			int m_CurrentBlock;
		public:
			reference_controler_pool_base()
			{
				m_CurrentBlock=0;
				m_CurrentBlockSize=BlockSize;
				MemoryBlocks[0]=new memory_block(m_CurrentBlockSize);
				m_CurrentBlockSize=(int)(1.1*(double)m_CurrentBlockSize);
			}
			~reference_controler_pool_base()
			{
				int Block=0;
				memory_block* pBlock=0;
				while((pBlock=MemoryBlocks[Block++])!=NULL)
				{
					delete pBlock;
				}
			}
			void delete_reference_controler(reference_controler* p)
			{
				p->reference_controler::~reference_controler();
				critical_section_lock lock(&m_CriticalSection);
				memory_block* pBlock=MemoryBlocks[m_CurrentBlock];
				if((memory_slot*)p>=pBlock->m_pMemorySlots && (memory_slot*)p<=pBlock->m_pMemorySlots+m_CurrentBlockSize)
				{
					pBlock->recover_free_slot((memory_slot*)p);
					return;
				}
				m_CurrentBlock=0;
				while(m_CurrentBlock<NumBlocks)
				{
					memory_block* pBlock=MemoryBlocks[m_CurrentBlock];
					if(NULL==pBlock)
						break;
					if((memory_slot*)p>=pBlock->m_pMemorySlots && (memory_slot*)p<=pBlock->m_pMemorySlots+m_CurrentBlockSize)
					{
						pBlock->recover_free_slot((memory_slot*)p);
						return;
					}
					m_CurrentBlock++;
				}
			}
			memory_slot* new_slot()
			{
				critical_section_lock lock(&m_CriticalSection);
				memory_slot* pSlot=MemoryBlocks[m_CurrentBlock]->give_free_slot();
				if(NULL==pSlot)
				{
					m_CurrentBlock=0;
					while(NULL==pSlot)
					{
						memory_block* pCurrentBlock=MemoryBlocks[m_CurrentBlock];
						if(pCurrentBlock)
							pSlot=MemoryBlocks[m_CurrentBlock]->give_free_slot();
						else
						{
							MemoryBlocks[m_CurrentBlock]=new memory_block(m_CurrentBlockSize);
							m_CurrentBlockSize=(int)(1.1*(double)m_CurrentBlockSize);
							pSlot=MemoryBlocks[m_CurrentBlock]->give_free_slot();
						}
						if(NULL==pSlot)
							m_CurrentBlock++;
					}
				}
				
				return pSlot;

			}
		};
		
		class o_memory_slot
		{
		public:
			LONG m_Last;
			LONG m_Next;
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
			void* m_pVoid;
#endif			
			o_memory_slot()
			{
				m_Last=0;
				m_Next=1;
				
			}
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
			virtual void delete_object(void* pVoid)
			{
			}
#endif
		};
		
		class reference_controler_pool : public reference_controler_pool_base<o_memory_slot>
		{
		public:
			
			template <class T>
			reference_controler* new_reference_controler(T* pT);

		};
		
		#ifdef MONITOR_REF_CONTROLERS
			static int stm_reference_controller_count;
		#endif
		#ifdef MONITOR_PTRS
			static int stm_owner_ptr_count;
			static int stm_ref_ptr_count;
			static int stm_strong_ptr_count;
			static int stm_deleter_count;
		#endif	
		static reference_controler_pool stm_RefControlerPool;
		
		LONG m_WeakCount;//Weak reference counter
		LONG m_StrongCount;//Strong reference counter but if -tive represents lock count

		void AddWeakRef()
		{
			if(m_WeakCount>0)
				m_WeakCount++;
			else
				InterlockedDecrement(&m_WeakCount);
		}
		
		void AddStrongRef()
		{
			if(m_WeakCount>0)
			{
				m_StrongCount++;
			}
			else
				InterlockedIncrement(&m_StrongCount);
		}
		
		void Lock()//Only lock weak reference - if it is already strong then no lock is added
		{
			if(m_WeakCount>0)//m_StrongCount<2)//if it is not already strong (+tive)
			{
				if(m_StrongCount>0)
					m_StrongCount=0;
				m_StrongCount--;//increase lock count (-tive)
			}
			else
				InterlockedIncrement(&m_StrongCount);//++;
		}
		
	public:
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES		
		virtual void delete_object()=0;
#endif
		reference_controler()
			: m_WeakCount(1), m_StrongCount(1)
		{
			
			#ifdef MONITOR_REF_CONTROLERS
				reference_controler::stm_reference_controller_count++;
			#endif	
		}

		~reference_controler()
		{
			//Only deleted when it has nothing more to do
			#ifdef MONITOR_REF_CONTROLERS
				reference_controler::stm_reference_controller_count--;
			#endif
		}

		
		bool ReleaseWeak()
		{
			if(m_WeakCount>0)
				--m_WeakCount;
			else
				++m_WeakCount;
			if(0==m_WeakCount && 0==m_StrongCount)
			{
				stm_RefControlerPool.delete_reference_controler(this);
				return true;
			}
			else
				return false;
		}
		

		bool ReleaseStrong()
		{
			if(0==--m_StrongCount)
			{
				return true;
			}
			else
				return false;
		}

		bool DeleteIfZeroCounts()
		{
			if(0==m_WeakCount && 0==m_StrongCount)
			{
				stm_RefControlerPool.delete_reference_controler(this);
				return true;
			}
			return false;
		}
		
		void Unlock()
		{
			if(m_StrongCount<0)//if it is a lock (+tive)
			{
				if(0==++m_StrongCount && 0==m_WeakCount)
					stm_RefControlerPool.delete_reference_controler(this);
				if(0==m_StrongCount)
					m_StrongCount=1;
					//delete this;
			}
			else if(ReleaseStrong())
				throw xonor_ptr_fatal_exception(_T("Object deleted while used by Fast Pointer"));

			//If strong count is +tive then no lock is needed
		}
	
		
	};
	
	template <class T>
	class reference_controler_impl : public reference_controler
	{
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
		T* m_pT;
#endif		
	public:
		reference_controler_impl(T* pT)
		{
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
			m_pT=pT;
#endif
		}
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
		virtual void delete_object()
		{
  
			delete m_pT;
 
			m_StrongCount=0;
		}
#endif		
	};


};//namespace PRIVATE

using namespace PRIVATE; 

//*************************************************************************************************
//Hidden classes used internally by pointer system but not visible in application code
//************************************************************************************************
template <class T> class smart_pointer_base //Base class for Auto, Weak and Fast pointers
{	
protected:
//Friends
	template<class U, class T2>	friend ref_ptr<T2> up_cast(ref_ptr<U>const& r);
	template<class U, class T2>	friend ref_ptr<T2> up_cast(owner_ptr_base<U>& apT);
	template<class U, class T2>	friend ref_ptr<T2> up_cast(ref_pointer_base<U>const& r);
						friend class ref_ptr<T>;
	template<class U>	friend class ref_ptr;
						friend class fast_ptr<T>;
	template<class U>	friend class fast_ptr;
						friend class ref_pointer_base<T>;
	template<class U>	friend class ref_pointer_base;

	mutable reference_controler* m_pReferenceControler;
	mutable T* m_pT;

	smart_pointer_base() {m_pReferenceControler=NULL; m_pT=NULL;}

	T* get_pointer_abs() const {return m_pT;}
	void set_null() const
	{
		if(m_pReferenceControler)
			m_pReferenceControler->ReleaseWeak();
		m_pReferenceControler=NULL;
		m_pT=NULL;
	}
	inline bool IsValidRef() const
	{
		if(m_pReferenceControler!= NULL && m_pReferenceControler->m_StrongCount!=0)
			return true;
		else
			set_null();
		return false;
	}
	

public:
	T* get_pointer()const {if(IsValidRef()) return m_pT; set_null(); return NULL;}
	
private:	
	//*****Methods to prevent operations which undermine the pointer system***
	void*  operator new(size_t s)	{BAD_CODE; "Never dynamically create a smart pointer"	}
	operator void*() const 	{BAD_CODE "Never delete a smart pointer" }
	operator smart_pointer_base<T>*()const {BAD_CODE  "Do not use pointers to smart pointers"} 
	
};

template <class T> class ref_pointer_base : public smart_pointer_base<T>
{
//Friends
						friend class ref_ptr<T>;
	template <class U>	friend class ref_ptr;
						friend class fast_ptr<T>;
	template <class U>	friend class fast_ptr;
						friend class strong_ptr<T>;
	template <class U>	friend class strong_ptr;

protected:
//Key operations
	ref_pointer_base()	{}
public:
	T* get_pointer()const {if(IsValidRef()) return m_pT; set_null(); return NULL;}
	
	//Equality test with all types
						bool operator==(smart_pointer_base<T>& wpT)const {return get_pointer()==wpT.get_pointer();}
	template<class U>	bool operator==(smart_pointer_base<U>const& wpT)const {return get_pointer()==static_cast<T*>(wpT.get_pointer());}
						bool operator==(T*const pT)const {return get_pointer()==pT;}
	//Inequality test with all types
						bool operator!=(smart_pointer_base<T>const& wpT)const {return get_pointer()!=wpT.get_pointer();}
	template<class U>	bool operator!=(smart_pointer_base<U>const& wpT)const {return get_pointer()!=static_cast<T*>(wpT.get_pointer());}
						
private:
	inline const T** operator&() 	
	{
		BAD_CODE ;//This would allow pointer value to be changed
	}

};


template <class T> class scoped_value : public smart_pointer_base<T>
{
//Friends
						friend class ref_ptr<T>;
	template<class U>	friend class ref_ptr;
						friend class ref_pointer_base<T>;
	template<class U>	friend class ref_pointer_base;
//Key operations
	reference_controler* AssureReferenceControler() const
	{
		//called when a reference is first taken from the owner
		if(NULL==m_pReferenceControler)
		{
			const_cast< reference_controler*>( m_pReferenceControler)=reference_controler::stm_RefControlerPool.new_reference_controler<T>(m_pT);
		}
		return m_pReferenceControler;
	}
	void reset()
	{
		if(m_pReferenceControler!=NULL)
		{
			//Test for fast_ptr locking this object (-tive strong count)
			if(m_pReferenceControler->m_StrongCount<0)
				throw xonor_ptr_fatal_exception(_T("Trying to delete while used by Fast Pointer"));
			
			m_pReferenceControler->m_StrongCount=0;
			if(m_pReferenceControler->DeleteIfZeroCounts())
				m_pReferenceControler=NULL;
			if(m_pReferenceControler!=NULL)
				m_pReferenceControler->ReleaseWeak();
		}
	}
public:
	scoped_value() { }
	~scoped_value() {reset();}
	
	scoped_value(T* pT)	{ m_pT=pT;	m_pReferenceControler=NULL;	}
	scoped_value* operator =(T* pT)	{ m_pT=pT; m_pReferenceControler=NULL; return this; }
	
	BOOL operator!() { return (m_pT == NULL) ? TRUE : FALSE ; }
	operator BOOL() const { return (m_pT != NULL) ? TRUE : FALSE ;}
	T* get_pointer() const {return m_pT ;}
	void OnPreDelete(T* pT)	{/*Derived classes can implement action to take before deletion*/}
};


template <class T> class owner_ptr_base : public smart_pointer_base<T>
{
//Friends
						friend class ref_ptr<T>;
	template <class U>	friend class ref_ptr;
						friend class fast_ptr<T>;
	template <class U>	friend class fast_ptr;
						friend class new_ptr<T>;
	template <class U, class T2>	friend ref_ptr<T2> up_cast(owner_ptr_base<U>& apT);
	
protected:
//Key operations
	template <class U>	void InnerCopy(owner_ptr_base<U>const& apT)
	{
		//Makes a copy of another owner_ptr
		//Will not complile if T does not have default constructor
		//and copy constructor
		if(apT.m_pT!=NULL)
		{
			m_pT=new T;//Create a new object
			*m_pT=*(apT.m_pT);//Copy members to new object
		}
	}
	void OnPreDelete(T* pT)
	{
		//Derived classes can implement action to take before deletion
	}
						inline void ThrowIfNull() const
	{
		//Defined response to trying to derefence Null pointer
		if(NULL==m_pT)
			throw xonor_ptr_exception(_T("NULL owner_ptr"));
	}
	template <class U>	reference_controler* AssureReferenceControler(U* pU) const
	{
		//called when a reference is first taken from the owner
		if(NULL==m_pReferenceControler)
		{
			const_cast< reference_controler*>( m_pReferenceControler)=reference_controler::stm_RefControlerPool.new_reference_controler<U>(pU);
		}
		return m_pReferenceControler;
	}
public:
	T* get_pointer() const	{return m_pT ;	}
	void reset()
	{
		if(m_pT!=NULL) 
		{ 
			if(m_pReferenceControler!=NULL)
			{
				//Test for fast_ptr locking this object (-tive strong count)
				if(m_pReferenceControler->m_StrongCount<0)
					throw xonor_ptr_fatal_exception(_T("Trying to delete while used by Fast Pointer"));
			
				
				if(m_pReferenceControler->ReleaseStrong())
				{
					OnPreDelete(m_pT);
#ifndef I_ALWAYS_USE_VIRTUAL_DESTRUCTORS_IN_POLYMORPHIC_BASE_CLASSES
					m_pReferenceControler->delete_object();
#else
					delete m_pT;
#endif
				}
				if(m_pReferenceControler->DeleteIfZeroCounts())
					m_pReferenceControler=NULL;
				if(m_pReferenceControler!=NULL)
					m_pReferenceControler->ReleaseWeak();
			}
			else
			{
				OnPreDelete(m_pT);
				delete m_pT; 
			}
			m_pT=NULL;
			m_pReferenceControler=NULL;
		}
		
		if(m_pReferenceControler!=NULL)
		{
			m_pReferenceControler->ReleaseWeak();//Release PtrHolder
			m_pReferenceControler=NULL;//and forget it
		}
	}
	
};

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
Spain Spain
Software Author with engineering, science and mathematical background.

Many years using C++ to develop responsive visualisations of fine grained dynamic information largely in the fields of public transport and supply logistics. Currently interested in what can be done to make the use of C++ cleaner, safer, and more comfortable.

Comments and Discussions