Click here to Skip to main content
15,881,882 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.3K   4.2K   73  
Describing a solution to allow namespaces, multiple inheritance, and serialization in an MFC program
// Provide serializazione type safe mechanisms, basd on templates (not macros)
// 
// to make an object serilizable do either
//	->	derive the object class from ETypeSerilizable<yourtype> specifying the class
//	->  declare at global level a varialble of type ETypeSerializable<yourtype>: this is necessary to make a fabric exist
//		even if no object of yourtype are alredy been created.

#pragma once

namespace GE_{namespace Safe{
    
	struct SFactory;
	
	//see ETypeSerializable, following
	class ESerializable //virtual base for every serializable class
	{
	protected:
		SFactory* _pLastFactory;	//the factory to be used to recreate the object
	public:
		ESerializable() { _pLastFactory =  NULL; }
		virtual void Serialize(CArchive& ar)=0; //serialize a derived object
		virtual SFactory* GetFactory() { return _pLastFactory; } //gets the object factory
	};

	//the abstract type factory base
	struct SFactory
	{
	protected:
		const type_info* _pTypeInfo; //RTTI must be ON !
	public:
		SFactory(void);
		~SFactory(void);
		CString GetTypeName() const;  //just return the name in the type_info
		static SFactory* FindFactory(CString name);  //search a factory in the chain whose GetTypeName() returns "name"
		virtual ESerializable* NewObject()=0; //the typed object creator !
	};

	//a typed Factory, used by serializable object
	template<class E>
	struct STypeFactory: public SFactory
	{
	protected:
	public:
		STypeFactory()
		{
			_pTypeInfo = &typeid(E);
		}
		virtual ESerializable* NewObject()  //mo more abstract, now !
		{
			return new E;
		}
	};

	//NOTE: virtual inheritance is required because in case of multiple inheritqance of 
	// various ETypeSerializable object, only one ESerializable instance must be present

	template <class E>
	class ETypeSerializable: public virtual ESerializable
	{
	private:
		SFactory* _pFactory;		//factory
		UINT& Count() {static UINT nCount = 0; return nCount;};		//how many are using it
	public:
		ETypeSerializable()
		{
			static STypeFactory<E>* pFactory = NULL; //the associated factory
			if(!pFactory)
			{	//we are the very first instance of this class
				pFactory = new STypeFactory<E>;
				Count() = 0;
			}
			_pFactory = pFactory;
			Count()++; //the number of instances created
			_pLastFactory = _pFactory;  //this value will be the last created factory for the object instance
		}
		virtual ~ETypeSerializable()
		{
			if(_pFactory)
			{
				Count()--;
				if(!Count())
					delete _pFactory; //no more used
			}
		}

		virtual void Serialize(CArchive& ar)
		{;} //default does nothig: derived object probably will
	};
	
	template<class E>
		void SavePtr(CArchive& ar, const E* pE)  //saves a pointer
	{
		SArchiveMaps* pM = SArchiveMaps::FromArchive(ar);
		pM->Save((E*)pE);
	}

	template<class E>
		void LoadPtr(CArchive& ar, E*& pE)   //loads a pointer
	{
		SArchiveMaps* pM = SArchiveMaps::FromArchive(ar);
		pM->Load(pE);
	}

	// create one of this class in every Serialize body
	struct SSerializeWatchDog;  //see later

	
	//serialization maps
	class SArchiveMaps
	{
	public:
		struct Exception {ESerializable* pE;}; //eventually throw this
	private:
		CArchive* _pAr; //the referred archive
		static std::map<CArchive*, SArchiveMaps*> _s_armaps; //find this from CArchive
		UINT _nID; //an ID
		std::map<LPVOID, UINT> _storemap; //"already stored" map
		std::map<UINT, LPVOID> _loadmap; //"already loaded" map (the reverse map. NOTE: maps on save and maps on load act as a relocation table)
		typedef std::pair<SSerializeWatchDog*, LPVOID> t_wdKey; //watchdogs AND watched objects
		std::map<t_wdKey, UINT> _watchdogmap; //[wathchdog & wathced] to ID
	public:
		static SArchiveMaps* FromArchive(CArchive& ar); //retrieve the ar associated maps
												//throws SArchiveMapsException* if not found
		SArchiveMaps(CArchive& ar);		//construt and associate to an Archive
		~SArchiveMaps();
		UINT MapObject(LPVOID pE); //add pE to load and store maps
		UINT MapWatchDog(SSerializeWatchDog& wd, LPVOID pInstance); //maps watch dogs and referred object
		LPVOID GetMappedObject(UINT nTag);  //from a tag to an object (looks _loadmap)

		template<class E>	 //saves a pointer
			void Save(E* pE) //throw SArchiveMaps::Exception*;
		{
			static SArchiveMaps::Exception excp;
			excp.pE = pE;
			if(!pE)
				(*_pAr) << (int)0;	//null pointer flag
			else
			{
				(*_pAr) << (int)1;	//good pointer flag

				UINT& m = _storemap[pE]; //gets a tag (will be 0 if not yet mapped)
				(*_pAr) << m; //save the tag
				if(!m)
				{// object not yet saved
					SFactory* pFactory = pE->GetFactory();  //gets the factory
					if(!pFactory) throw &excp; //cannot save an unlodable object
					(*_pAr) << pFactory->GetTypeName(); //save the type name
					pE->Serialize(*_pAr); //save the contents and map the objects
					if(!m) throw &excp; //m now should have a value
					(*_pAr) << m; //save the tag
				}
			}
		}

		template<class E>	//loads back a pointer
			void Load(E*& rpE) //throw SArchiveMaps::Exception*;
		{
			static SArchiveMaps::Exception excp;
			excp.pE = NULL;
			int isvalid; (*_pAr) >> isvalid;  //the null flag
			if(!isvalid)
			{
				rpE = NULL;
				return;
			}
			
			//non null pointer
			UINT m; 
			(*_pAr) >> m; //gets the ID
			if(!m) //if 0, we must create a new object
			{
				CString name; (*_pAr) >> name; //get the type name
				SFactory* pFactory = SFactory::FindFactory(name); //locate the fabric
				if(!pFactory) throw &excp; // cannot create!
				ESerializable* pES = pFactory->NewObject(); //create the object (we don't know the type, but the factory knows)
				ASSERT(pES); //must exist !
				pES->Serialize(*_pAr); //serialize the object and map the object into maps
				(*_pAr) >> m;	//gets the real ID
				ASSERT(m);	//must exist !
			}
			rpE = (E*)_loadmap[m]; //sets return value, taken from the map
			ASSERT(rpE);	//must be not null
		}
	};

	
	struct SSerializeWatchDog
	{
	public:
		bool Locked(CArchive& ar, LPVOID pInstance); //if true, you have already serialized
	};

	//shortcut macro (can be used in Serialize functions)
	//	declare a woatchdog representing the body and instance, and locks it
	//	forces a return if already locked (body already executed on a same instance)
#define GE_SERIALIZE_CHECK_MULTIPLE(ar)\
	{\
		static GE_::Safe::SSerializeWatchDog wd;\
		if(wd.Locked(ar, this)) return;\
	}

	// the thrown exception
	class SArchiveMapsException: public CException
	{
	public:
		SArchiveMapsException(bool bAutodelete=true);
		static void Throw();
		virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError, PUINT pnHelpContext=NULL);
	};

}}

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