|
// 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.
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.