Generic Serializer - serializing arbitrary data structures






4.33/5 (5 votes)
Aug 12, 2000

102371

2912
Template functions for serializing arbitrary linked nodes.
Introduction
The methods in the MFC classes CObject
and CArchive
provide a framework for serialization but give no help when one has to serialize
complex data structures containing cyclic references.
The functions GenericSerialize
and GenericDeserialize
presented
in this article implement a general serialization algorithm. These
C++ template functions can handle any homogenous datastructure (datastructure consisting of
nodes having all the same type).
The user has to provide a so called Accessor class which is responsible for
- defining a typedef name called
Type
which gives the type of a node pointer. - providing a member variable called
ar
which must be a reference to an archive object. - providing a member function
Null()
which returns the null value forType
- serializing and deserializing a single element.
- returning the immediate neighbors of an element.
Interface
template<class Access> void GenericSerialize(const Access& a); template<class Access> void GenericDeserialize(const Access& a); //The Access class must adhere to the following scheme struct MyAccess { typedef MyPointerType Type; CArchive ar; Type Null() {/*your code*/} void GetNeighbors(Type current,vector< pair<Type,int/*nTyp*/> >& vecNeighbors) const { /*your code */ }//GetNeighbors(Null(),.. ) must place all root elements // into <code>vecNeighbors void SerializeThis(Type owner,Type current,int nTyp)const { /*your code/* } Type DeserializeThis(Type owner,int nTyp)const { /*your code*/ } void SetReference(Type owner,Type current,int nTyp)const { /*your code*/ }//current is an already deserialized reference of owner };
Example A - Serialization of a tree view control
The demo project serializes a tree view control by a call to the following function.
void CGenericSerializeDemoView::TreeSerialize(CArchive& ar) { TreeAccess access(m_Tree,ar); //user defined part of serialization if (ar.IsStoring()) { GenericSerialize(access);//general algorithm for serialization } else { m_Tree.DeleteAllItems(); GenericDeserialize(access); //general algorithm for deserialization ApplyItemStates(m_Tree.GetRootItem()); } }
The following Accessor class is the user provided part for the serialization of a tree view control.
struct TreeAccess{//user part for tree ctrl serialization TreeAccess(CTreeCtrl& rTree,CArchive& rAr):tree(rTree),ar(rAr){} typedef HTREEITEM Type; //'Type' is a mandatory member of any Access class #define TA_eRoot 0 #define TA_eChild 1 #define TA_eSibling 2 CTreeCtrl& tree; CArchive& ar; //'ar' is a mandatory member of any Access class HTREEITEM Null()const {return NULL;} void GetNeighbors(HTREEITEM current,vector< pair<Type,int> >& vecNeighbors) const { if(current==NULL){//get root items HTREEITEM hItem= tree.GetRootItem(); if(hItem){ vecNeighbors.push_back( make_pair(hItem,TA_eRoot)); } }else{ HTREEITEM hItem= tree.GetNextSiblingItem(current); if(hItem){ vecNeighbors.push_back(make_pair(hItem,TA_eSibling)); } hItem= tree.GetChildItem(current); if(hItem){ vecNeighbors.push_back(make_pair(hItem,TA_eChild)); } } } void SerializeThis(HTREEITEM owner,HTREEITEM current,int nTyp)const { ar << tree.GetItemText(current); ar << tree.GetItemState(current,TVIF_STATE); } HTREEITEM DeserializeThis(HTREEITEM owner,int nTyp)const { CString strItemText; UINT nState; ar >> strItemText; ar >> nState; HTREEITEM hItem; switch(nTyp){ case TA_eRoot: hItem= tree.InsertItem(strItemText); break; case TA_eChild: hItem= tree.InsertItem(strItemText,owner); break; case TA_eSibling: hItem= tree.InsertItem(strItemText, tree.GetParentItem(owner),owner); break; } tree.SetItemData(hItem,nState); return hItem; } void SetReference(HTREEITEM owner,HTREEITEM current,int nTyp)const { } };
Example B - Serialization of a general graph
The following Accessor class is the user provided part for the serialization of a Graph consisting of nodes having the following definition
struct Graph{ Graph(int x=0,int y=0):x(x),y(y){} int x,y; vector<Graph*> vecConnections; };
struct GraphAccess{//user part for graph serialization GraphAccess(vectorGraph& rGraph,CArchive& rAr):graph(rGraph),ar(rAr){} typedef Graph* Type; //'Type' is a mandatory member of any Access class vectorGraph& graph; CArchive& ar; //'ar' is a mandatory member of any Access class #define GA_eRoot 0 #define GA_eConnect 1 Graph* Null()const {return 0;} void GetNeighbors(Graph* current,vector< pair<Type,int> >& vecNeighbors)const { if(current==NULL){//get root items for(unsigned i=0;i<graph.size();i++){ vecNeighbors.push_back( make_pair(graph[i],GA_eRoot)); } }else{ for(unsigned i=0;i<current->vecConnections.size();i++){ vecNeighbors.push_back( make_pair(graph[i],GA_eConnect)); } } } void SerializeThis(Graph* owner,Graph* current,int nTyp)const { ar << current->x; ar << current->y; } Graph* DeserializeThis(Graph* owner,int nTyp)const { int x,y; ar >> x; ar >> y; graph.push_back(new Graph(x,y)); return graph[graph.size()-1]; } void SetReference(Graph* owner,Graph* current,int nTyp)const { if(owner!=0 && nTyp==GA_eConnect){ owner->vecConnections.push_back(current); } } };