65.9K
CodeProject is changing. Read more.
Home

Generic Serializer - serializing arbitrary data structures

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (5 votes)

Aug 12, 2000

viewsIcon

102371

downloadIcon

2912

Template functions for serializing arbitrary linked nodes.

  • Download demo project - 25 Kb
  • Download source - 3 Kb
  • Sample Image

    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 for Type
    • 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);
            }
        }
    };
    
    Generic Serializer - serializing arbitrary data structures - CodeProject