天天看点

boost之序列化学习(一)

最近在研究boost这个库,这个库很强大,鉴于工作的需要,我花了点时间学习了下boost库里的序列化机制,个人感觉很强大,boost库对序列化这块支持的范围很广,其中对于xml这方面的序列化机制感觉支持的很不错,下面就从最基础的开始吧,代码如下:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/list.hpp>
#include <iostream>
#include <fstream>

void save()
{
    std::ofstream file("archive.txt");
    boost::archive::text_oarchive oa(file);
    std::string s = "Hello world";
    //oa << s;
    oa & s;
}

void load()
{
    std::ifstream file("archive.txt");
    boost::archive::text_iarchive ia(file);
    std::string s;
    //ia >> s;
    ia & s;
    std::cout << s << std::endl;
}
           

在boost中比较常用的序列化机制主要分为文本时和二进制形式,而文本时的序列化机制又分为了纯文本式和xml式,而上面的例子就是一个纯文本式的序列化机制,里面用到了boost库的text_oarchive和text_iarchive,并且在用boost做序列化时我们一般都会设置一些序列化载体,纯文本式的序列化机制其载体就是单个的纯文本文件,而xml式的载体则是xml文件,至于二进制序列化其载体就不确定了,一般都是内存buffer结构,有时也会写文件,在上述代码中,我们首先定义好了载体,然后与相应的序列化对象进行关联,并通过相应的运算符操作来实现相关的序列化操作,接下来,我们来看看xml式的序列化机制,代码如下:

void save_XML()
{
    std::ofstream file("test.xml");
    boost::archive::xml_oarchive oa(file);
    std::string s = "Hello world";
    oa & BOOST_SERIALIZATION_NVP(s);
}

void load_XML()
{
    std::ifstream file("test.xml");
    boost::archive::xml_iarchive ia(file);
    std::string s;
    ia & BOOST_SERIALIZATION_NVP(s);
    std::cout << s <<std::endl;
}
           

在以xml为载体的序列化中,会使用到xml_oarchive和xml_iarchive,并且还会使用到BOOST_SERIALIZATION_NVP这个宏,在这个地方我们先不对这些对象以及宏进行深入讨论,在下篇博文中,我们会深入分析boost库的Serialization这个类,接下来,我们稍微来看看基于xml式序列化机制的一些测试用例吧,代码如下:

1. 针对数组和STL

void save_Array()
{
    std::ofstream file("test1.xml");
    boost::archive::xml_oarchive oa(file);
    int array1[] = {12,23,34,4,343};
    oa & BOOST_SERIALIZATION_NVP(array1);
}

void load_Array()
{
    std::ifstream file("test1.xml");
    boost::archive::xml_iarchive ia(file);
    int restored[5];
    ia & BOOST_SERIALIZATION_NVP(restored);
    std::ostream_iterator<int> oi(std::cout," ");
    std::copy(restored,restored+5,oi);
}

void save_STL()
{
    std::ofstream file("test2.xml");
    boost::archive::xml_oarchive oa(file);
    int array[] = {23,45,51,23,34,45};
    std::vector<int> vec(array,array+6);
    std::list<int> lst(array,array+6);
    oa & BOOST_SERIALIZATION_NVP(vec);
    oa & BOOST_SERIALIZATION_NVP(lst);
}

void load_STL()
{
    std::ifstream file("test2.xml");
    boost::archive::xml_iarchive ia(file);
    std::list<int> L2;
    ia & BOOST_SERIALIZATION_NVP(L2);
    std::vector<int> V2;
    ia & BOOST_SERIALIZATION_NVP(V2);

    std::ostream_iterator<int> oi(std::cout," ");
    std::copy(L2.begin(),L2.end(),oi);
    std::copy(V2.begin(),V2.end(),oi);
}
           

其结果:

1)针对数组

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<array1>
    <count>5</count>
    <item>12</item>
    <item>23</item>
    <item>34</item>
    <item>4</item>
    <item>343</item>
</array1>
</boost_serialization>
           

2)针对STL

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<vec>
    <count>6</count>
    <item_version>0</item_version>
    <item>23</item>
    <item>45</item>
    <item>51</item>
    <item>23</item>
    <item>34</item>
    <item>45</item>
</vec>
<lst>
    <count>6</count>
    <item_version>0</item_version>
    <item>23</item>
    <item>45</item>
    <item>51</item>
    <item>23</item>
    <item>34</item>
    <item>45</item>
</lst>
</boost_serialization>
           

2. 针对自定义结果序列化

struct date
{
    date()
    {
        year = month = day = 0;
    }
    date(int year,int month,int day)
    {
        this->year = year;
        this->month = month;
        this->day = day;
    }

/*    template<class Archive>
    void serialize(Archive& archive,const unsigned int version)
    {
        //archive & year;
        //archive & month;
        //archive & day;
        archive & BOOST_SERIALIZATION_NVP(year);
        archive & BOOST_SERIALIZATION_NVP(month);
        archive & BOOST_SERIALIZATION_NVP(day);
    }
    */
    int year;
    int month;
    int day;
};
/*
namespace boost
{
    namespace serialization{
        template<class Archive>
        void serialize(Archive& archive,date& data,const unsigned int version)
        {
            archive & BOOST_SERIALIZATION_NVP(data.year);
            archive & BOOST_SERIALIZATION_NVP(data.month);
            archive & BOOST_SERIALIZATION_NVP(data.day);
        }
    }
}*/
           

在针对自定义结构的序列化中,其有两种方式来实现对成员的序列化操作,其一:直接在自定义类型中实现serialize这个函数,其二:将serialize实现放置在自定义类型之外,这两种实现方式分别对应着两种称为:1)入侵式序列化,2)非入侵式序列化,这两种序列化各有优缺点,使用入侵式序列化需要在结构体中实现serialize函数,这样就破坏了类的封装,使用非入侵式序列化,需要将类中的成员变量设置为public,这样就对外暴露了类的实现,至于如何取舍,关键是在于程序的设计者,结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<da class_id="0" tracking_level="0" version="0">
    <data.year>2014</data.year>
    <data.month>2</data.month>
    <data.day>8</data.day>
</da>
</boost_serialization
           

3. 针对自定义结构的指针序列化

    对自定义结构指针序列化主要是针对指针所指定的实际内容进行序列化,在这方面boost库Serialization类做的还算不错,代码如下:

void save_Pointer()
{
    std::ofstream file("Object3.xml");
    boost::archive::xml_oarchive oa(file);
    date* dat = new date(2014,2,10);
    oa & BOOST_SERIALIZATION_NVP(dat);
}

void load_Pointer()
{
    std::ifstream file("Object3.xml");
    boost::archive::xml_iarchive ia(file);
    date* dr;
    ia & BOOST_SERIALIZATION_NVP(dr);
    std::cout <<"year:"<<dr->year << "month:"<< dr->month <<"day:"<<dr->day<<std::endl;
}
           

其使用方式基本上跟序列化其他的基础类型差不多,使用超级方便,测试结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<dat class_id="0" tracking_level="1" version="0" object_id="_0">
    <data.year>2014</data.year>
    <data.month>2</data.month>
    <data.day>10</data.day>
</dat>
</boost_serialization>
           

在转储指针时,我们需要注意一点就是当转储的指针有多个时,并且这些指针所指的地址一致时,这时候,转储文件会是怎样的呢,先来看看代码:

void save_MulPointer()
{
           
.    std::ofstream file("Object4.xml");
    boost::archive::xml_oarchive oa(file);
    date* dat = new date(2014,2,10);
    date* tmp = dat;

    date d(2014,2,11);
    date& ref = d;
    oa & BOOST_SERIALIZATION_NVP(dat);
    oa & BOOST_SERIALIZATION_NVP(tmp);

    oa & BOOST_SERIALIZATION_NVP(d);
    oa & BOOST_SERIALIZATION_NVP(ref);

}

void load_MulPointer()
{
    std::ifstream file("Object4.xml");
    boost::archive::xml_iarchive ia(file);
    date* dr,*tm;
    ia & BOOST_SERIALIZATION_NVP(dr);
    ia & BOOST_SERIALIZATION_NVP(tm);
    std::cout<<"year:"<<dr->year<<"month:"<<dr->month<<"day:"<<dr->day<<std::endl;
    std::cout<<"year:"<<tm->year<<"month:"<<tm->month<<"day:"<<tm->day<<std::endl;
}
           

在这个案例中,分别针对指针和引用进行序列化,测试结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<dat class_id="0" tracking_level="1" version="0" object_id="_0">
    <data.year>2014</data.year>
    <data.month>2</data.month>
    <data.day>10</data.day>
</dat>
<tmp class_id_reference="0" object_id_reference="_0"></tmp>
<d object_id="_1">
    <data.year>2014</data.year>
    <data.month>2</data.month>
    <data.day>11</data.day>
</d>
<ref object_id_reference="_1"></ref>
</boost_serialization>
           

从测试结果可以看出,针对指针和引用的序列化其实都是只有一个对象,并通过使用引用计数的方式来实现对象的共享。

4. 将serialize操作分为save和load操作

    在这种操作中主要是将我们的serialize函数实现拆分为两个子函数来实现,因为在前面的案例中,我们的序列化操作都是实现serialize函数,现在我们需要实现save和load函数,这样的好处就是能够将原有的序列化机制进行细分,这样有利于后续相关功能的扩展,代码如下:

struct date
{
    date()
    {
        year = month = day = 0;
    }
    date(int year,int month,int day)
    {
        this->year = year;
        this->month = month;
        this->day = day;
    }

/*    template<class Archive>
    void serialize(Archive& archive,const unsigned int version)
    {
        //archive & year;
        //archive & month;
        //archive & day;
        archive & BOOST_SERIALIZATION_NVP(year);
        archive & BOOST_SERIALIZATION_NVP(month);
        archive & BOOST_SERIALIZATION_NVP(day);
    }
    */
    template<class Archive>
    void save(Archive& archive,const unsigned int version) const
    {
        archive & BOOST_SERIALIZATION_NVP(year);
        archive & BOOST_SERIALIZATION_NVP(month);
        archive & BOOST_SERIALIZATION_NVP(day);
    }

    template<class Archive>
    void load(Archive& archive,const unsigned int version)
    {
        archive & BOOST_SERIALIZATION_NVP(year);
        archive & BOOST_SERIALIZATION_NVP(month);
        archive & BOOST_SERIALIZATION_NVP(day);
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
    int year;
    int month;
    int day;
};
           

在date类中,我们需要实现save函数和load函数,并且save函数需要实现为const函数的形式,在定义函数的末尾我们需要添加BOOST_SERIALIZATION_SPLIT_MEMBER这个宏,测试结果如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="11">
<dat class_id="0" tracking_level="1" version="0" object_id="_0">
    <year>2014</year>
    <month>2</month>
    <day>10</day>
</dat>
</boost_serialization>
           

继续阅读