您的位置:首页 > 其它

Boost::Serialization试用手记

2014-10-21 17:42 155 查看
对于核心数据相对比较集中的应用程序来说,serialization机制可以直接充当文档保存与打开功能的实现工具,这也是很多成熟的应用程序framework都提供serialization支持的原因

但是个人认为,serialization最精彩的用处在于保存现场,比如在探索性的科研应用程序开发过程中,很可能一部分算法已经固定下来,其余的有待进一步探索,而确定下来的部分有可能十分time consuming,如果每次改一下算法都要从头计算,就会很费时间,长期下去对工作情绪会有很大影响,这时候可以用serialization把每一步的结果存成文件,下次启动时任选一个开始新的计算。

Boost的Serialization库是一个十分强大的工具,它文档中提到的11个开发目标列举如下:

1.代码移植性,只依赖于ANSI C++标准

2.代码简洁性,的确运用Boost::Serialization所需要的代码量很小

3.每个类有自己独立的版本控制,以保证旧的save结果可以被新的程序load回来

4.深度指针save与load,不仅serialize指针本身,而且包括它指向的对象数据

5.多个指针指向同一个对象不会被serialize多次

6.对常用STL容器的支持

7.串行数据的平台移植性

8.类如何被串行化与串行数据按何种格式存储相互无关

9.非侵入性,这对于数据中使用了第三方类库的情形很有效

最后两个没有理解,希望高人指点

10.The archive interface must be simple enough to easily permit creation of a new type of archive. 

11.The archive interface must be rich enough to permit the creation of an archive that presents serialized data as XML in a useful manner.

下面是我试用Boost::Serialization的记录

第一个例子

假设有这样一个类需要串行化

 


 class  Data


 {


 public :


     int  mInt;


} ;

 

这里为了简化起见,成员变量都设成public了,串行化的代码如下

 

 1 

 #include  < fstream > 
 2 

 
 3 

 //  fewest include headers 
 4 

 #include  < boost / archive / text_iarchive.hpp > 
 5 

#include  < boost / archive / text_oarchive.hpp > 
 6 

 
 7 

 //  use this to ease the archive selection 
 8 

 typedef boost::archive::text_iarchive iarchive;
 9 

typedef boost::archive::text_oarchive oarchive;
10 

 
11 

 class  Data
12 

 {
13 

    friend  class  boost::serialization::access;
14 

 
15 

 protected :
16 

 
17 

    template < class  Archive > 
18 

      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )
19 

      {
20 

        ar  &  mInt;
21 

    } 
22 

 
23 

 public :
24 

 
25 

     int  mInt;
26 

 
27 

     static   void  saveData( const  Data &  obj, std:: string  fileName)
28 

      {
29 

        std::ofstream ofs(fileName.c_str());
30 

        oarchive oa(ofs);
31 

        oa  <<  obj;
32 

    } 
33 

 
34 

     static   void  loadData(Data &  obj, std:: string  fileName)
35 

      {
36 

        std::ifstream ifs(fileName.c_str());
37 

        iarchive ia(ifs);
38 

        ia  >>  obj;
39 

    } 
40 

} ;
41 

 
42 

 void  main()
43 

 {
44 

    Data d1;
45 

    d1.mInt  =   3 ;
46 

 
47 

    Data::saveData(d1,  " output.txt " );
48 

 
49 

    Data d2;
50 

    Data::loadData(d2,  " output.txt " );
51 

 
52 

     //  results should be the same. 
53 

     ToolLib::LOG(TOSTR(d2.mInt));
54 

}

 

成员serialize函数是定义类如何被串行化之规则的核心。

由于使用了RTTI机制,serialize函数不需要为virtual,永远只要是void就可以,在串行化指针的时候能够被正确调用。

serialize函数中的 & 运算符在load时调用 >>,而在save时调用 <<,这样save和load只要一个函数就可以。

saveData和loadData函数必不可少,由于serialization库强大的编译检查机制,如果不用这样的方式来save一个对象,往往会因为这个对象不是const而编译失败。

所用的archive类型可以任选,如text或binary,上例中的typedef即是为了封装这一变化

非侵入性

下面一例演示了串行化不可侵入的类型。这里用的是 WildMagicLib2.5中的 2-Vector

 


 #include  < WildMagic2p5 / Include / WmlVector2.h > 


 


 class  Data


 {


    friend  class  boost::serialization::access;




 protected :




    template < class  Archive > 


      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )


     {


        ar  &  mVec2d;


    } 


 


 public :




    Wml::Vector2d mVec2d;




     static   void  saveData( const  Data &  obj, std:: string  fileName);


     static   void  loadData(Data &  obj, std:: string  fileName);


} ;




 namespace  boost  { 


 namespace  serialization  {


    template < class  Archive,  class  Real > 


     void  serialize(Archive &  ar, Wml::Vector2 < Real >&  g,  const  unsigned  int  version)


     {


        ar  &  g.X();


        ar  &  g.Y();


    } 


}   //  namespace serialization 


 }   //  namespace boost



 

这种情况下,需要这个全局serialize在能够访问到那个类里需要串行化的数据,常常load和save的方法不一样,如load时调用setVar,save时调用getVar,这时Archive::is_loading和Archive::is_saving常数就有用了。一种等价但是更直观的方法是使用BOOST_SERIALIZATION_SPLIT_MEMBER或者BOOST_SERIALIZATION_SPLIT_FREE宏,两者分别生成调用load/save成员函数和load/save全局函数的代码。

在serialization内部,是通过定义全局serialize函数模板,并在里面调用成员serialize函数来实现的,如下所示,需要非侵入的对象只要特化这个全局函数就可以了。

 


 //  default implemenation - call the member function "serialize" 


 template < class  Archive,  class  T > 


inline  void  serialize(


    Archive  &  ar, T  &  t,  const  BOOST_PFTO unsigned  int  file_version


) {


    access::serialize(ar, t, static_cast < unsigned  int > (file_version));


}

 

STL容器支持

通过包含一些serialization提供的头文件,对STL容器可以像普通变量一样支持

 


 //  STL support headers 


 #include  < boost / serialization / vector.hpp > 


#include  < boost / serialization / string .hpp > 


 


 class  Data


 {


    friend  class  boost::serialization::access;




 protected :




    template < class  Archive > 


      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )


     {


        ar  &  mStr;


        ar  &  mVecInt;


        ar  &  mVecStr;


    } 


 


 public :




    std:: string  mStr;


    std::vector < int >  mVecInt;


    std::vector < std:: string >  mVecStr;




     static   void  saveData( const  Data &  obj, std:: string  fileName);


     static   void  loadData(Data &  obj, std:: string  fileName);


} ;

 

指针与数组

 


 class  ClassA


 {


 public :


     int  mInt;


} ;




 class  Data


 {


    friend  class  boost::serialization::access;




 protected :




    template < class  Archive > 


      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )


     {


        ar  &  mPtrInt;


        ar  &  mArrInt;


        ar  &  mPtrData;


        ar  &  mPtrA;


    } 


 


 public :




    Data():mPtrData(NULL), mPtrInt(NULL), mPtrA(NULL) {} 


 


    Data *  mPtrData;


     int  mArrInt[ 10 ];


     int *  mPtrInt;


    ClassA *  mPtrA;




     static   void  saveData( const  Data &  obj, std:: string  fileName);


     static   void  loadData(Data &  obj, std:: string  fileName);


} ;




 namespace  boost  { 


 namespace  serialization  {


    template < class  Archive > 


     void  serialize(Archive &  ar,  int &  g,  const  unsigned  int  version)


     {


        ar  &  g;


    } 


}   //  namespace serialization 


 }   //  namespace boost



 

数组可以直接串行化,指针比须保证有效,所以必须保证在串行化之前经过初始化。

对于基本类型如int,可以直接串行化,但其指针int*,要当作不可侵入类型的指针来看待,所以需要一个全局serialize函数来说明int类型的串行化方式

对于有基类指针的串行化,代码如下

 


 class  ClassA


 {


 public :


     int  mIntA;




     virtual   void  someMethod()  =  NULL;


    


    template < class  Archive > 


      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )


     {


        ar  &  mIntB;


    } 


} ;




BOOST_IS_ABSTRACT(ClassA)




 class  ClassB:  public  ClassA


 {


 public :


     int  mIntB;


    


     virtual   void  someMethod() {} 


 


    template < class  Archive > 


      void  serialize(Archive  &  ar,  const  unsigned  int   /*  file_version  */ )


     {


        ar  &  boost::serialization::base_object < ClassA > ( * this );


        ar  &  mIntB;


    } 


} ;




BOOST_CLASS_EXPORT(ClassB)

 

纯虚类后加上BOOST_ISABSTRACT,而可能会被串行化到的子类用BOOST_CLASS_EXPORT,这样就可以在任何地方串行化 ClassA* 的成员变量。

子类的serialize函数里必须要照顾到基类的成员。

版本控制

在serialize函数中的version参数就是用于版本控制的,所有类的版本号默认为0,新版本的类可以自己指定版本号以便与旧版本相区别。如下

BOOST_CLASS_VERSION(ClassA, 1)

对于save过程,版本号始终为新的,而load过程取决于文件中保存的值,对于新版本新增变量的情况可以这样解决

 


 //  old definition 


 class  ClassA


 {


 public :


     int  mInt;


} ;




 //  new definition 


 class  ClassA


 {


 public :


     int  mInt;


     int  mIntNew;




    template < class  Archive > 


     void  serialize(Archive  &  ar,  const  unsigned  int  ver)


     {


        ar  &  mInt;




         if (ver  ==   1 )


            ar  &  mIntB;


    } 


} ;




BOOST_CLASS_VERSION(ClassA,  1 )

 

对于有改动的情形,稍微复杂一点,可以这样

 


 //  old definition 


 class  ClassA


 {


 public :


    TypeA mVarA;


} ;




 //  new definition 


 class  ClassA


 {


 public :




     //  suppose now we use B and C instead of A 


     TypeB mVarB;


    TypeC mVarC;




    template < class  Archive > 


     void  serialize(Archive  &  ar,  const  unsigned  int  ver)


     {


         if (ver  <   1 )


         {


             //  here must be loading 


 


            TypeA varA;


            ar  &  varA;


            


             //  now derive mVarB & mVarC from varA; 


         } 


         else 


          {


            ar  &  mVarB;


            ar  &  mVarC;


        } 


    } 


} ;




BOOST_CLASS_VERSION(ClassA,  1 )

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: