Click here to Skip to main content
15,885,004 members
Articles / Desktop Programming / MFC

MFC - Multiple inheritance and serialization

Rate me:
Please Sign up or sign in to vote.
4.80/5 (34 votes)
29 Oct 2003CPOL14 min read 163.4K   4.2K   73  
Describing a solution to allow namespaces, multiple inheritance, and serialization in an MFC program
//Smartptr.h	include file for various kind of smart pointers
//
//		first version July 2003
//		1.1		- october 2003: added deletion functor
//

#pragma once

namespace GE_{namespace Safe{


	//***********************************************************
	//the exception thrown if trying to dereference a null smartptr
	struct XRefcnt_ptr_excp
	{
		static void Throw()  //just trow an XRefcnt_ptr_excp*
		{
			static XRefcnt_ptr_excp e;
			throw &e;
		};
	};

	//***********************************************************

	
	//anticipate the pointers declarations
	template <class T>	class _PtrSmart;
	template <class T, class C>	class PtrStrong;
	template <class T, class C>	class PtrWeak;


	// #### UPDATE 1.1
	////////////////////////
	// deletion functor: called to delete an object
	template<class T>
		struct FDeletor
		{
			// by default just calls delete. Specialize if you need different behaviour
			void operator()(T* pT) {delete pT;}
		};

	//common base (interface) for all referene counter

	namespace{ //unnamed namespace, to localize this internal data

		struct RefCount_base
		{
			virtual ~RefCount_base() {;}
			virtual void IncStrong()=0;		//increments strong counter
			virtual void IncWeak()=0;		//increment weak counter
			virtual bool ReleaseStrong()=0;	//decrements strong counter (and release if 0)
			virtual bool ReleaseWeak()=0;	//decrement weak counter (and release if 0)
			virtual bool IsNull()=0;		//true if not representing a referred object
			virtual LONG GetRefCount()=0;	//gets the reference coutn value
		};

		//the reference count structure
		template<class R>
		struct RefCount: public RefCount_base
		{
		private:
			LONG _cntStrong;	//strong counter
			LONG _cntWeak;		//weak counter
			R* _ptr;			//referered aggregator
		public:
			RefCount(R* pR) //creates reference counters
			{
				_cntStrong = _cntWeak = 0;
				_ptr = pR;
			}
			~RefCount()
			{	ASSERT(!_cntStrong && !_cntWeak);	}
			virtual void IncStrong()
			{	InterlockedIncrement(&_cntStrong);			}
			virtual void IncWeak()
			{	InterlockedIncrement(&_cntWeak);			}
			virtual bool ReleaseStrong()   //return true if the object ir deleted
			{
				InterlockedDecrement(&_cntStrong);
				if(! _cntStrong)
				{
					IncWeak(); //protecte this refcount from deleting
					if(_ptr)
					//	### Update 1.1
					//	delete _ptr; //NOTE: destructors may call ReleaseWeak;
						FDeletor<R>()(_ptr);
					// ### end
					_ptr = NULL;

					ReleaseWeak();  //end of protection: if weak goes to zero also delete this
					return true;
				}
				return false;
			}
			virtual bool ReleaseWeak()	//always true
			{
				InterlockedDecrement(&_cntWeak);
				if(! _cntWeak)
				{
					if(!_cntStrong) //nobody is looking at "this"
 						delete this;	//suicide
				}
				return true;
			}
			
			virtual bool IsNull() {	return !_ptr;	};
			virtual LONG GetRefCount() { return _cntStrong;}

			R* GetPtr()	const {return _ptr;}

			//### update 1.1
			// called to set all pointers to NULL;
			void MakeNull() { _ptr = NULL;}
			// END
		};

		//the common virtual base for all "samrt objects"
		//	NOTE:	a complicated object may have many bases and many smartpointer types to various subobjects
		//	each of the bases may be EObject derived, however, we must assure that only one refounter is present
		//	hence, ObjStrong_base must be inherited as "virtual"

		//a signature is required to identify if an object converts correctly into ObjStrong_base.
		// when RTTI is used this is not necessary (dynamic_cast dies the same better)
		class ObjStrong_base
		{
		private:
			TCHAR _signature[9];
			static LPCTSTR GetSignature() {return "_ObjS10_";}
		protected:
			RefCount<ObjStrong_base>* _pRfcnt;

			ObjStrong_base() 
			{
				lstrcpyn(_signature, GetSignature(), 9); 
				_pRfcnt = new RefCount<ObjStrong_base>(this);
				_pRfcnt->IncWeak();
			}
		public:
			//### update 1.1
			virtual void Destroy() { if(!_pRfcnt->GetRefCount()) delete this; };
			//### end 

			virtual ~ObjStrong_base()
			{
				if(_pRfcnt)
				{
					//### update 1.1
					// if a strong object is foprced to be deleted, when smart pointers are still around
					// we can set to null the reference. This makes all the pointer to become null.
					_pRfcnt->MakeNull();
					/// END

					_pRfcnt->ReleaseWeak();
				}
			}

			bool IsStrong() const {return lstrcmp(_signature,GetSignature()) == 0;}
			RefCount_base* GetRefCounters() const {return _pRfcnt;}
			LONG GetRefCount() const {return _pRfcnt->GetRefCount();}
		};

	}

	// ### UPDATE 1.1
	////////////////////////
	// deletion functor: specilization for ObjStrong_base
	//
	template<>
		struct FDeletor<ObjStrong_base>
		{
			// call Destroy(). By default will do "delete this"
			void operator()(ObjStrong_base* pT) {pT->Destroy();}
		};
	/// END
	
	
	//*******************************************************************************
	//use this base for objects you want to be convertible from "dumb" to "smart" or "weak" pointers
	//	derive every object you wanto to be saf in conversion between "dumb" and "smart" poiters 

	class ObjStrong: public virtual ObjStrong_base
	{
	};

	namespace {
		
		//global helper

		template<class T>
		RefCount_base* GetRefCountBase(T* pT)
		{
			ObjStrong_base* pStrong = (ObjStrong_base*)pT; //unsafe cast
			if( !pStrong->IsStrong()) //unmatched signature
				return NULL;
			return pStrong->GetRefCounters();
		}

	}

	//*******************************************************************************
	//the reference couter smart pointers bases
	
	//for convenience a same base is defined for all smart pointers
	class _PtrSmart_base{};

	//a typed common base for all sart pointers
	template<class T>
	class _PtrSmart: public _PtrSmart_base
	{
		friend class PtrStrong;
		friend class PtrWeak;
	protected:
		T* _pT; //the referenced object; NOTE: it may be different than the referred by RefCount, because it may be a sub or super object
		RefCount_base* _pRefCnt;  //reference counter

		//initialization
		void _Init()
		{
			_pT = NULL;
			_pRefCnt = NULL;
		}
		_PtrSmart() { _Init();	}
		virtual ~_PtrSmart() {;}
	public:
		bool IsNull() const { return !_pRefCnt || _pRefCnt->IsNull(); }
		//dereferencing
		operator T*() const {return (IsNull())? NULL: _pT;} //NOTE: _pT may be not NULL, but if IsNull() is true, must be considered "invalid"
		T* operator->() const { if(IsNull()) XRefcnt_ptr_excp::Throw(); return _pT; }
		T& operator*() const { if(IsNull()) XRefcnt_ptr_excp::Throw(); return *_pT; }
		bool operator!() const { return IsNull(); }
		LONG GetRefCount() { return _pRefCnt->GetRefCount(); }
		virtual Set(T* pT)=0;

		//the behaviour changes from strong to weak
	};


	//here is a default for PtrConvert, doing nothing
	// you can customize this if you have lots of convertion fron a signle type
	//specilizing the calls for the "From" class, and the operaqtor for the "To" classes
	template<class U> //from type
	struct PtrConvert
	{
		template<class T> //to type
		void operator() (T*& pT, U*pU) const
		{	pT = NULL;	} //default does not convert
	};

	//and here is a template function, doing nothing
	// you can specialize for "To - From" types couple
	template<class T, class U> // U = from, T = to
	T* FPtrConvertFn(U* pU)
	{
		return NULL;
	};

	//here are the type casting functors
	template<class T>
	struct FDynamicCast
	{
		template<class U>
		T operator() (U u) {return dynamic_cast<T>(u);}
	};

	template<class T>
	struct FStaticCast
	{
		template<class U>
		T operator() (U u) {return static_cast<T>(u);}
	};


	template<
		class T,   //the "to" class
		class U,   //the "from" class
		class C  //the default "caster"
	>	
	T* smart_cast(U* pU)
	{
		T* pT = NULL;
		
		//try using the function
		pT = FPtrConvertFn<T,U>(pU);
		if(pT)
			return pT;	//successful conversion
		
		//try using functional
		PtrConvert<U>()(pT, pU);
		if(pT) 
			return pT; //successfull conversion

		return C()(pU);  //use the type caster
	};


	//####################################
	//	PtrStrong
	//	it can refer "smart"(ObjStrong derived) or "weak" objects.
	//	It can also do comparison between poiters and objects

	template
	<
		class T,		//the class this pointer is for
#ifdef _CPPRTTI
		class C = FDynamicCast<T*>  //the type caster towards T*
#else
		class C = FStaticCast<T*>
#endif
	>		
	class PtrStrong: public _PtrSmart<T>
	{
		friend class _PtrSmart;

	private:

		void _Clear()  //cleanup and set to null
		{
			if(_pRefCnt)
				_pRefCnt->ReleaseStrong();
			_pRefCnt = NULL;
			_pT = NULL;
		}

		//assign from "dumb pointer" 
		//(avoid to do this from non-strong objects, unless it is the very first assignment)
		void _Assign(T* pT)  
		{
			if(_pT == pT) return;	//return immediatly if reassign

			_Clear();	//cleanup (release and set to null)

			if(pT)
			{
				_pRefCnt = GetRefCountBase<T>(pT);
				if(!_pRefCnt) 
				{
					//may be the object is not "smart", 
					//	do a new refcounter (## less safe! ##)
					_pRefCnt = new RefCount<T>(pT);
				}
				_pRefCnt->IncStrong();
				_pT = pT;
			}
		}

		template<class U>
		void _Assign(U* pU)  //eterogeneus dumb assign
		{
			T* pT = smart_cast<T,U,C>(pU);
			if(_pT == pT) return;

			_Clear();

			if(pT)
			{
				_pRefCnt = GetRefCountBase<U>(pU);
				if(!_pRefCnt) 
				{
					//may be the object is not "smart", 
					//	do a new refcounter (## less safe! ##)
					_pRefCnt = new RefCount<U>(pU);
				}
				_pRefCnt->IncStrong();
				_pT = pT;
			}
		}

		void _Assign(const _PtrSmart<T>& cpsT) //from another smart pointer
		{
			bool bToNull = cpsT.IsNull();
			if(!bToNull && _pT == cpsT._pT) return;

			_Clear();
			if(!bToNull)
			{
				_pT = cpsT._pT;
				_pRefCnt = cpsT._pRefCnt;
				_pRefCnt->IncStrong();
			}
		}

		template<class U>
			void _Assign(const _PtrSmart<U>& cpsU)
		{
			bool bToNull = cpsU.IsNull();
			T* pT = NULL; 
			if(!bToNull)
				pT = smart_cast<T,U,C>(cpsU._pT);
			if(pT && _pT == pT) return;

			_Clear();

			if(pT)
			{
				//use the same counter, but refer _pT
				_pRefCnt = cpsU._pRefCnt;
				_pT = pT;
				_pRefCnt->IncStrong();
			}
		}
		
	public:
		//public "user" functions
		
		//cunstructor, assignment and conversions
		PtrStrong() {;}
		~PtrStrong() {_Clear();};
		PtrStrong(const PtrStrong& cpsT) {_Assign(cpsT);}
		PtrStrong(const _PtrSmart<T>& cpsT) {_Assign(cpsT);}
		PtrStrong(const T* pT) {_Assign((T*)pT);}
		template<class U> PtrStrong(const U* pU) {_Assign<U>((U*)pU);}
		template<class U> PtrStrong(const _PtrSmart<U>& cpsU) {_Assign<U>(cpsU);}
		PtrStrong<T>& operator=(const PtrStrong& cpsT) {_Assign(cpsT); return *this;}
		PtrStrong<T>& operator=(const _PtrSmart<T>& cpsT) {_Assign(cpsT); return *this;}
		PtrStrong<T>& operator=(const T* pT) {_Assign((T*)pT); return *this;};
		template<class U> PtrStrong<T>& operator=(const U* pU) {_Assign<U>((U*)pU); return *this;}
		template<class U> PtrStrong<T>& operator=(const _PtrSmart<U>& cpsU) {_Assign<U>(cpsU); return *this;}

		T* New() {T* pT = new T; _Assign(pT); return pT;}
		template<class U> T* New(const U& u) {T* pT = new T(u); _Assign(pT); return pT;}
		template<class U, class V> T* New(const U& u, const V& v) {T* pT = new T(u,v); _Assign(pT); return pT;}
		template<class U, class V, class W> T* New(const U& u, const V& v, const W& w) {T* pT = new T(u,v,w); _Assign(pT); return pT;}
		void Null() {_Clear();};
		virtual Set(T* pT) { _Assign(pT); }
	};


	//####################################
	//	PtrWeak
	//	it can refer "smart"(ObjStrong derived) or "weak" objects.
	//	It can also do cpomparison between poiters and objects

	template
		<
			class T,				//the class this pointer is for
#ifdef _CPPRTTI
		class C = FDynamicCast<T*>  //the type caster towards T*
#else
		class C = FStaticCast<T*>
#endif
		>
	class PtrWeak: public _PtrSmart<T>
	{
		void _Clear()
		{
			if(_pRefCnt)
				_pRefCnt->ReleaseWeak();
			_pRefCnt = NULL;
			_pT = NULL;
		}

		void _Assign(T* pT)
		{
			if(_pT == pT) return;

			_Clear();

			if(pT)
			{
				_pRefCnt = GetRefCountBase<T>(pT);
				if(!_pRefCnt) 
				{
					//may be the object is not "smart", 
					//or no provision from aggregation is made.
					//	do a new refcounter (## less safe! ##)
					_pRefCnt = new RefCount<T>(pT);
				}
				_pRefCnt->IncWeak();
				_pT = pT;
			}
		}

		template<class U>
		void _Assign(U* pU)
		{
			T* pT = smart_cast<T,U,C>(pU);
			if(_pT == pT) return;

			_Clear();

			if(pT)
			{
				_pRefCnt = GetRefCountBase<U>(pU);
				if(!_pRefCnt) 
				{
					//may be the object is not "smart", 
					//or no provision from aggregation is made.
					//	do a new refcounter (## less safe! ##)
					_pRefCnt = new RefCount<U>(pU);
				}
				_pRefCnt->IncWeak();
				_pT = pT;
			}
		}

		void _Assign(const _PtrSmart<T>& cpsT)
		{
			bool bToNull = cpsT.IsNull();
			if(!bToNull && _pT == cpsT._pT) return;

			_Clear();
			if(!bToNull)
			{
				_pT = cpsT._pT;
				_pRefCnt = cpsT._pRefCnt;
				_pRefCnt->IncWeak();
			}
		}

		template<class U>
			void _Assign(const _PtrSmart<U>& cpsU)
		{
			bool bToNull = cpsU.IsNull();
			T* pT = NULL; 
			if(!bToNull)
				pT = smart_cast<T,U,C>(cpsU._pT);
			if(pT && _pT == pT) return;

			_Clear();

			if(pT)
			{
				//use the same counter, but refer _pT
				_pRefCnt = cpsU._pRefCnt;
				_pT = pT;
				_pRefCnt->IncWeak();
			}
		}
		
	public:

		//public "user" functions
		
		PtrWeak() {;}
		~PtrWeak() {_Clear();};
		PtrWeak(const PtrWeak& cpsT) {_Assign(cpsT);}
		PtrWeak(const _PtrSmart<T>& cpsT) {_Assign(cpsT);}
		PtrWeak(const T* pT) {_Assign((T*)pT);}
		template<class U> PtrWeak(const U* pU) {_Assign<U>((U*)pU);}
		template<class U> PtrWeak(const _PtrSmart<U>& cpsU) {_Assign<U>(cpsU);}
		PtrWeak<T>& operator=(const PtrWeak& cpsT) {_Assign(cpsT); return *this;}
		PtrWeak<T>& operator=(const _PtrSmart<T>& cpsT) {_Assign(cpsT); return *this;}
		PtrWeak<T>& operator=(const T* pT) {_Assign((T*)pT); return *this;};
		template<class U> PtrWeak<T>& operator=(const U* pU) {_Assign<U>((U*)pU); return *this;}
		template<class U> PtrWeak<T>& operator=(const _PtrSmart<U>& cpsU) {_Assign<U>(cpsU); return *this;}
		void Null() { _Clear(); };
		virtual Set(T* pT) { _Assign(pT); }
	};


	//comparison

	//****** dafault comparison implementation ******
	
	//equal is by pointers. specialize differently if it is the case
	template<class T>
		bool SmartPtrEqual(const T* p1, const T* p2)
	{	return std::equal_to<const T*>()(p1,p2);	}

	//less is by referred value. Specialize it differently if it is the case
	template<class T>
		bool SmartPtrLess(const T* p1, const T* p2)
	{	try {return std::less<T>()(*p1,*p2);} catch(...){;} return false;	}


	//equality and sorting comparisons
	template<class T>
		bool operator == (const _PtrSmart<T>& p1, const _PtrSmart<T>& p2)
	{	return SmartPtrEqual<T>(p1, p2);	};

    template<class T>
		bool operator == (const _PtrSmart<T>& p1, const T* p2)
	{	return SmartPtrEqual<T>(p1, p2);	};

    template<class T>
		bool operator == (const T* p1, const _PtrSmart<T>& p2)
	{	return SmartPtrEqual<T>(p1, p2);	};

    template<class T>
		bool operator < (const _PtrSmart<T>& p1, const _PtrSmart<T>& p2)
	{	return SmartPtrLess<T>(p1, p2);	};

    template<class T>
		bool operator < (const _PtrSmart<T>& p1, const T* p2)
	{	return SmartPtrLess<T>(p1, p2);	};

    template<class T>
		bool operator < (const T* p1, const _PtrSmart<T>& p2)
	{	return SmartPtrLess<T>(p1, p2);	};


	//other operator are defined in term of these operators in the std namespace,
	// in the <utility> STL header.

}}






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
Architect
Italy Italy
Born and living in Milan (Italy), I'm an engineer in electronics actually working in the ICT department of an important oil/gas & energy company as responsible for planning and engineering of ICT infrastructures.
Interested in programming since the '70s, today I still define architectures for the ICT, deploying dedicated specific client application for engineering purposes, working with C++, MFC, STL, and recently also C# and D.

Comments and Discussions