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

Generic Serializer - serializing arbitrary data structures

Rate me:
Please Sign up or sign in to vote.
4.33/5 (5 votes)
11 Aug 2000 101.9K   2.9K   60   4
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</code>
        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);
            }
        }
    };

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    Switzerland Switzerland
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    GeneralUnable to download Source Files Pin
    Neeraj Sathe26-Jun-05 18:49
    Neeraj Sathe26-Jun-05 18:49 
    Questionconverting a sequence of BMP to AVI ??? Pin
    mr20037-May-01 2:59
    mr20037-May-01 2:59 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.