boost::serialization 序列化库详解

前言

在项目实现过程中, 需要实现数据的结构化输出与读入. 比如将一个 classstruct 包含的数据输出到文件中, 然后还要读取回来.
上述过程可以用 序列化 (serialization) 来实现. 下面记录一下阅读boost::serialization官方demo的过程. 内容主要是对其Tutorial的翻译.
序列化(serialization) 是指将一组任意的 C++ 数据结构可逆地解构为一个字节序列.这可以用来在某个程序环境中重建一个等价的解构.具体来说, 我们用 存档 (archive) 一词来指代这种字节序列的呈现. 这种序列可以是二进制数据, 文本数据, XML, 或者是由用户创建的文件.

Tutorial

输出存档类似于输出数据流.可以用运算符 << 或者 & 输出数据到存档中:

ar << data;
ar & data;

输入存档类似于输出存档.可以用运算符 >> 或者 & 从存档中读取数据:

ar >> data;
ar & data;

当这些运算符被原始数据类型调用时, 数据简单地保存到存档中或从存档中加载.当被类数据类型调用时, 会调用类的序列化函数. 每个序列化函数都使用上述的运算符来保存或加载类的数据成员. 该过程以递归的方式进行, 直到类中包含的所有数据都被保存或加载.

A Very Simple Case

这些运算符在序列化函数中保存或者是加载类的数据成员. demo.cpp说明了如何使用序列化这个库. 下面我们摘录这个 demo.cpp 的代码, 用最简单的情况说明这个库的使用方法.

#include <fstream>

// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

/
// gps coordinate
//
// illustrates serialization for a simple type
//
class gps_position
{
private:
    friend class boost::serialization::access;
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;
public:
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};

int main() {
    // create and open a character archive for output
    std::ofstream ofs("filename");

    // create class instance
    const gps_position g(35, 59, 24.567f);

    // save data to archive
    {
        boost::archive::text_oarchive oa(ofs);
        // write class instance to archive
        oa << g;
    	// archive and stream closed when destructors are called
    }

    // ... some time later restore the class instance to its orginal state
    gps_position newg;
    {
        // create and open an archive for input
        std::ifstream ifs("filename");
        boost::archive::text_iarchive ia(ifs);
        // read class state from archive
        ia >> newg;
        // archive and stream closed when destructors are called
    }
    return 0;
}

对于每个需要通过序列化来保存的类, 必须有一个函数来保存这个类定义的所有数据成员. 对于每个需要通过序列化加载的类,必须有一个函数按照保存的顺序来加载这些数据成员. 在上面的例子中, 这些函数是由模板成员函数 serialize() 生成的.

Non Intrusive Version

上述的实现是侵入性的. 也就是说,要修改那些实例被序列化的类的定义.在某些情况下, 这可能是不太方便的. 另一种等效的, 可行的表述是:

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

class gps_position
{
public:
    int degrees;
    int minutes;
    float seconds;
    gps_position(){};
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}
};

namespace boost {
namespace serialization {

template<class Archive>
void serialize(Archive & ar, gps_position & g, const unsigned int version)
{
    ar & g.degrees;
    ar & g.minutes;
    ar & g.seconds;
}

} // namespace serialization
} // namespace boost

在这个例子中, 序列化函数不再是 class gps_position 的成员, 但实现的功能完全相同的.
非侵入性序列化的主要应用是允许在不修改类定义的情况下为类实现序列化. 但为了实现这个效果, 类必须暴露足够多的信息来重建.这个例子中, 我们假定这个类有公共成员–这不是一个常见的情况. 只有那些暴露足够的信息以保存和重建本身的类才能在无需改变类的定义的情况下实现序列化.

Serializable Members

一个具有可序列化成员的可序列化类如下所示:

class bus_stop
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & latitude;
        ar & longitude;
    }
    gps_position latitude;
    gps_position longitude;
protected:
    bus_stop(const gps_position & lat_, const gps_position & long_) :
    latitude(lat_), longitude(long_)
    {}
public:
    bus_stop(){}
    // See item # 14 in Effective C++ by Scott Meyers.
    // re non-virtual destructors in base classes.
    virtual ~bus_stop(){}
};

也就是说, class 类型的成员可以像原始类型的成员一样被序列化.
需要注意的是, 用一个存档运算符保存一个 bus_stop 类的实例将调用 serialize 函数, 该函数将保存经纬度(latitude longitude). 然后, 将通过调用 gps_position 中定义的 serialize 来保存具体的定位值(degrees minutes seconds). 通过这种方式, 应用存档运算符, 整个数据结构被保存为其的根项.

Derived Classes

派生类应该包括其基类的序列化.

#include <boost/serialization/base_object.hpp>

class bus_stop_corner : public bus_stop
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // serialize base class information
        ar & boost::serialization::base_object<bus_stop>(*this);
        ar & street1;
        ar & street2;
    }
    std::string street1;
    std::string street2;
    virtual std::string description() const
    {
        return street1 + " and " + street2;
    }
public:
    bus_stop_corner(){}
    bus_stop_corner(const gps_position & lat_, const gps_position & long_,
        const std::string & s1_, const std::string & s2_
    ) :
        bus_stop(lat_, long_), street1(s1_), street2(s2_)
    {}
};

请注意基类从派生类中的序列化. 不要直接调用基类的序列化函数. 这样做可能看起来很有效, 但会绕过追踪写入存储的实例的代码, 以消除冗余. 它也会绕过将类的版本信息写入存档. 处于这个原因, 我们最好是将成员的序列化函数变成私有的.
声明 friend boost::serialization::access 将授予序列化库对私有变量和函数的访问权限.

Pointers

假设我们将一条公交车线路定义为一些公交车站的组合. 基于: \

  1. 我们可能有几种类型的公交车站(记住 bus_stop 是一个基类).
  2. 一个给定的公交车站可能出现在多条线路中.

用指向 bus_stop 的指针的数组来表示一条公交车线路.

class bus_route
{
    friend class boost::serialization::access;
    bus_stop * stops[10];
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        int i;
        for(i = 0; i < 10; ++i)
            ar & stops[i];
    }
public:
    bus_route(){}
};

stops 数组的每一个成员都将被序列化. 但是请记住每个成员都是一个指针–那这到底意味着什么呢? 这种序列化的目标是为了能够在另一个地方和时间重建原始数据结构. 为了用指针来实现这一点, 仅仅保存指针的值是不够的, 必须保存它所指向的对象. 当后来加载时, 必须创建一个新的对象, 并将一个新的指针加载到类成员中.
如果同一个指针被序列化了不止一次, 那么只有一个实例被添加到存档中. 当读回时, 没有数据被读回. 唯一发生的操作是, 第二个指针被设为等于第一个指针.
注意到, 在这个例子中, stops 数组由多态指针组成. 也就是说, 每个数组元素都指向几种可能的公交车站中的一个. 因此, 当指针被保存时, 必须保存某种类型的标识符. 当指针加载回来时, 必须读取类的标识符, 并且必须构建相应类的实例. 最后, 数据可以加载到新创建的正确类型的实例中. 在demo.cpp中可以看到, 通过基类指针对派生类的序列化, 可能需要对序列化的派生类进行明确的枚举. 这被称作派生类的 “注册” 或 “导出” .这里将详细解释这一要求和满足这一要求的方法.
这些操作都是由序列化库自动完成的. 上述代码是对通过指针访问的对象完成保存和加载操作的全部必要条件.

Arrays

上面的表述其实没必要那么复杂. 序列化库会检测到被序列化的对象是一个数组, 并发出等同于上述的代码. 所以上面的内容可以简化为:

class bus_route
{
    friend class boost::serialization::access;
    bus_stop * stops[10];
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & stops;
    }
public:
    bus_route(){}
};

STL Collections

上面的例子使用了一个成员的数组. 更有可能的是, 这样的应用可以用一个STL集合来实现. 序列化库包含了所有STL类的序列化的代码. 因此, 下面的重述也正如期望一般实现了.

#include <boost/serialization/list.hpp>

class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & stops;
    }
public:
    bus_route(){}
};

Class Versioning

假设我们对 bus_route 类很满意, 建立了一个使用它的程序, 并发行了产品. 一段时间后, 我们决定对程序进行改进, 于是对 bus_route 类进行了修改, 加入了该路线的司机的名字, 因此新的版本如下所示:

#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>

class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & driver_name;
        ar & stops;
    }
public:
    bus_route(){}
};

很好, 我们都完成了. 除了…使用我们程序的人, 现有有一堆在以前的程序下创建的存档文件, 怎么办? 这些文件如何在我们的新程序版本中使用?
一般来说, 序列化库为每个序列化的类在存档中存储了一个版本号. 默认情况下, 这个版本号是 0. 当加载存档文件时, 会读取它保存的版本号. 可以利用这一点对上面的代码进行修改:

#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>

class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        // only save/load driver_name for newer archives
        if(version > 0)
            ar & driver_name;
        ar & stops;
    }
public:
    bus_route(){}
};

BOOST_CLASS_VERSION(bus_route, 1)

通过对每个类应用版本管理, 就不需要试图去维护文件的版本. 也就是说, 一个文件的版本是其所有构成类别的版本的组合. 这个系统允许程序始终与所有以前旧版本的程序创建的存档兼容, 而不需要做出比这个例子更多的工作.

Splitting serialize into save/load

序列化函数简单、简洁, 并保证类成员以相同的顺序被保存和加载–这是序列化系统的关键. 然而, 在有些情况下, 加载和保存操作并不和这里的例子那么相似. 例如, 这可能发生在一个经过多个版本更迭演变的类中. 上述的类可以重新表述为:

#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>

class bus_route
{
    friend class boost::serialization::access;
    std::list<bus_stop *> stops;
    std::string driver_name;
    template<class Archive>
    void save(Archive & ar, const unsigned int version) const
    {
        // note, version is always the latest when saving
        ar  & driver_name;
        ar  & stops;
    }
    template<class Archive>
    void load(Archive & ar, const unsigned int version)
    {
        if(version > 0)
            ar & driver_name;
        ar  & stops;
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
    bus_route(){}
};

BOOST_CLASS_VERSION(bus_route, 1)

BOOST_SERIALIZATION_SPLIT_MEMBER() 宏产生的代码, 根据存档是用于保存还是加载来调用 saveload 函数.

Archives

我们讨论的重点是为类添加序列化能力. 要被序列化的数据实际的渲染是在 archive 类中实现的. 因此, 序列化的数据流是类的序列化和被选存档的产物. 这是一个关键的设计选择, 即这两个组件是独立的. 这允许任何序列化规范可用于任何存档.
在本教程中, 我们使用了一个特殊的归档类-- text_oarchive 用于保存而 text_iarchive 用于加载. 文本存档以文本的形式呈现数据, 可以跨平台移植. 除了文本存档, 该库还包括用于本地二进制数据和XML格式数据的存档类. 所有存档类的接口都是相同的. 一旦为一个类定义了序列化, 该类可以被序列化为任何类型的存档.
如果当前的存档类不能提供特定应用所需的属性、格式或行为, 可以制作一个新的存档类或从现有的存档类中派生出来. 这将在本手册的后面描述.

List of Examples

  • demo.cpp
    这是本教程中完整的例子. 它做了以下事情:

    1. 创建了一个由不同种类的站点, 路线和时间计划表组成的结构
    2. 显示该结构
    3. 将其序列化为一个名为 testfile.txt 的文件
    4. 从文件中加载回复到另一个结构
    5. 显示回复后的结构

    这个程序的输出足以验证这个系统是否满足了最初说的对序列化系统的所有要求. 存档文件的内容也可以显示, 因为序列化文件是ASCII文本.

  • demo_xml.cpp
    这是 demo 的变体. 除了其他类型, 还支持XML存档. BOOST_SERIALIZATION_NVP(name) 这个额外包装的宏, 将数据项的名称与相应的xml标签联系起来. 重要的是, name 是一个有效的xml标签, 否则无法恢复存档. 更多的信息请参考Name-Value Pairs. 这里是一个xml存档的例子.

  • demo_xml_save.cppdemo_xml_load.cpp
    还要注意的是, 尽管我们的例子是在同一个程序中保存和加载数据到一个存档中, 但这只是为了方便说明问题. 在一般情况下, 存档可能被创建它的程序加载, 也可能不被加载.

聪明的读者可能会注意到, 这些例子包含一个微妙但重要的缺陷. 它们泄露了内存. 公交车站是在主函数中创建的. 公交车计划时间表可以多次引用这些公交车站. 在主函数结束时, 公交车计划时间表被销毁后, 公交车站也被销毁. 这看起来似乎很好. 但是, 在从存档中加载的过程中创建的结构 new_schedule 又是怎么回事呢? 它包含了自己独立的公交车站, 这些站点在公交车计划时间表之外没有被引用. 它们不会在程序中任何地方被销毁–内存泄漏.
有几种方法可以解决这个问题. 一种方法是明确地管理公交车站. 然而, 一个更稳健和透明的方法是使用 shared_ptr 而不是原始指针. 除了标准库的序列化实现之外, 序列化库还包括 boost::shared_ptr 的序列化实现. 鉴于此, 改变这些例子中的任何一个内存泄漏应该是很容易的. 这是留给读者的一个练习题. (解答例子)


© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


个人阅读记录

最后是我阅读demo过程中在其中加上一些注释后的内容.

/1/2/3/4/5/6/7/8
// demo.cpp
//
// (C) Copyright 2002-4 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/tmpdir.hpp>
#include <boost/serialization/assume_abstract.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>     // std::list
#include <boost/serialization/utility.hpp>  // std::pair
#include <cstddef>                          // NULL
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>

/
// The intent of this program is to serve as a tutorial for
// users of the serialization package.  An attempt has been made
// to illustrate most of the facilities of the package.
//
// The intent is to create an example suffciently complete to
// illustrate the usage and utility of the package while
// including a minimum of other code.
//
// This illustration models the bus system of a small city.
// This includes, multiple bus stops,  bus routes and schedules.
// There are different kinds of stops.  Bus stops in general will
// will appear on multiple routes.  A schedule will include
// muliple trips on the same route.

/
// gps coordinate
//
// llustrates serialization for a simple type
class gps_position {  // save the position just like a struct which save several
                      // data
  friend std::ostream &operator<<(std::ostream &os, const gps_position &gp);
  friend class boost::serialization::access;  // use friend Keyword then boost
                                              // can get the class data.
  int degrees;
  int minutes;
  float seconds;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int /* file_version */) {
    ar &degrees &minutes &seconds;
  }

 public:
  // every serializable class needs a constructor
  gps_position(){};
  gps_position(int _d, int _m, float _s)
      : degrees(_d), minutes(_m), seconds(_s) {}
};
// overload `<<` to output gps_position data
std::ostream &operator<<(std::ostream &os, const gps_position &gp) {
  return os << ' ' << gp.degrees << (unsigned char)186 << gp.minutes << '\''
            << gp.seconds << '"';  // ASCII 186 `°`
}

/
// One bus stop
//
// illustrates serialization of serializable members
//

class bus_stop {  // use `gps_position` to indicate location with longitude and
                  // latitude
  friend class boost::serialization::access;
  friend std::ostream &operator<<(std::ostream &os, const bus_stop &gp);
  // pure virtual functions define like:
  // `virtual std::string description() = 0;`
  // A pure virtual function cannot be implemented,
  // it can only be given to a derived class to inherit
  // and then the derived class to populate it.
  // pure virtual function not defined in the base class,
  // but any derived class is required to define its own implementation methods
  // `const` means that `this` is a pointer to a constant,
  // its members are not able to change.
  virtual std::string description() const = 0;  // pure virtual function
  gps_position latitude;
  gps_position longitude;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    ar &latitude;
    ar &longitude;
  }

 protected:
  bus_stop(const gps_position &_lat, const gps_position &_long)
      : latitude(_lat), longitude(_long) {}

 public:
  bus_stop() {}
  // The destructor in the base class must be a virtual function,
  // otherwise an object release error will occur.
  virtual ~bus_stop() {}
};

// When serializing an object through a pointer to its base class,
// the library needs to determine whether or not the base is abstract
// (i.e. has at least one virtual function).
// The library uses the type trait macro BOOST_IS_ABSTRACT(T) to do this.
// Not all compilers support this type trait and corresponding macro.
// To address this, the macro BOOST_SERIALIZATION_ASSUME_ABSTRACT(T) has
// been implemented to permit one to explicitly indicate that a specified
// type is in fact abstract. This will guarentee that
// BOOST_IS_ABSTRACT will return the correct value for all compilers.
BOOST_SERIALIZATION_ASSUME_ABSTRACT(bus_stop)

std::ostream &operator<<(std::ostream &os, const bus_stop &bs) {
  return os << bs.latitude << bs.longitude << ' ' << bs.description();
}

/
// Several kinds of bus stops
//
// illustrates serialization of derived types
//
class bus_stop_corner
    : public bus_stop {  // indicate a bus stop in street corner
  friend class boost::serialization::access;
  std::string street1;
  std::string street2;
  virtual std::string description() const {  // define the virtual function
    return street1 + " and " + street2;
  }
  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    // save/load base class information
    ar &boost::serialization::base_object<bus_stop>(*this);
    ar &street1 &street2;
  }

 public:
  bus_stop_corner() {}
  bus_stop_corner(const gps_position &_lat, const gps_position &_long,
                  const std::string &_s1, const std::string &_s2)
      : bus_stop(_lat, _long), street1(_s1), street2(_s2) {}
};

// indicate the bus stop at the end of the route
class bus_stop_destination : public bus_stop {
  friend class boost::serialization::access;
  std::string name;
  virtual std::string description() const {
    return name;
  }  // define the virtual function
  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    ar &boost::serialization::base_object<bus_stop>(*this);
    ar &name;
  }

 public:
  bus_stop_destination() {}
  bus_stop_destination(const gps_position &_lat, const gps_position &_long,
                       const std::string &_name)
      : bus_stop(_lat, _long), name(_name) {}
};

/
// illustrates serialization of STL collection templates.
//
// illustrates serialzation of polymorphic pointer (bus stop *);
//
// illustrates storage and recovery of shared pointers is correct
// and efficient.  That is objects pointed to by more than one
// pointer are stored only once.  In such cases only one such
// object is restored and pointers are restored to point to it
// a bus route is a collection of bus stops
class bus_route {
  friend class boost::serialization::access;
  friend std::ostream &operator<<(std::ostream &os, const bus_route &br);
  // use typedef define bus_stop_pointer == bus_stop *
  typedef bus_stop *bus_stop_pointer;
  std::list<bus_stop_pointer> stops;  // include several stations

  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    // in this program, these classes are never serialized directly but rather
    // through a pointer to the base class bus_stop. So we need a way to be
    // sure that the archive contains information about these derived classes.
    // bus_stop_corner and bus_stop_destination are derived class of the base
    // class bus_stop ar.template register_type<bus_stop_corner>();
    ar.register_type(static_cast<bus_stop_corner *>(NULL));
    // ar.template register_type<bus_stop_destination>();
    ar.register_type(static_cast<bus_stop_destination *>(NULL));
    // serialization of stl collections is already defined in the header
    ar &stops;  // std::list
  }

 public:
  bus_route() {}
  void append(bus_stop *_bs) { stops.insert(stops.end(), _bs); }
};
std::ostream &operator<<(std::ostream &os, const bus_route &br) {
  std::list<bus_stop *>::const_iterator it;  // const_iterator
  // note: we're displaying the pointer to permit verification
  // that duplicated pointers are properly restored.
  for (it = br.stops.begin(); it != br.stops.end(); it++) {
    os << '\n' << std::hex << "0x" << *it << std::dec << ' ' << **it;
  }
  return os;
}

/
// a bus schedule is a collection of routes each with a starting time
//
// Illustrates serialization of STL objects(pair) in a non-intrusive way.
// See definition of operator<< <pair<F, S> >(ar, pair) and others in
// serialization.hpp
//
// illustrates nesting of serializable classes
//
// illustrates use of version number to automatically grandfather older
// versions of the same class.

class bus_schedule {
 public:
  // note: this structure was made public. because the friend declarations
  // didn't seem to work as expected.
  struct trip_info {
    template <class Archive>
    void serialize(Archive &ar, const unsigned int file_version) {
      // in versions 2 or later
      if (file_version >= 2)
        // read the drivers name
        ar &driver;
      // all versions have the follwing info
      ar &hour &minute;
    }

    // starting time
    int hour;
    int minute;
    // only after system shipped was the driver's name added to the class
    std::string driver;

    trip_info() {}
    trip_info(int _h, int _m, const std::string &_d)
        : hour(_h), minute(_m), driver(_d) {}
  };

 private:
  friend class boost::serialization::access;
  friend std::ostream &operator<<(std::ostream &os, const bus_schedule &bs);
  friend std::ostream &operator<<(std::ostream &os,
                                  const bus_schedule::trip_info &ti);
  // include several routes with time and driver information
  std::list<std::pair<trip_info, bus_route *> > schedule;
  template <class Archive>
  void serialize(Archive &ar, const unsigned int version) {
    ar &schedule;
  }

 public:
  void append(const std::string &_d, int _h, int _m, bus_route *_br) {
    schedule.insert(schedule.end(), std::make_pair(trip_info(_h, _m, _d), _br));
  }
  bus_schedule() {}
};
BOOST_CLASS_VERSION(bus_schedule::trip_info, 2)

std::ostream &operator<<(std::ostream &os, const bus_schedule::trip_info &ti) {
  return os << '\n' << ti.hour << ':' << ti.minute << ' ' << ti.driver << ' ';
}
std::ostream &operator<<(std::ostream &os, const bus_schedule &bs) {
  std::list<std::pair<bus_schedule::trip_info, bus_route *> >::const_iterator
      it;
  for (it = bs.schedule.begin(); it != bs.schedule.end(); it++) {
    os << it->first << *(it->second);  // it->second is a bus_route pointer
  }
  return os;
}

void save_schedule(const bus_schedule &s, const char *filename) {
  // make an archive
  std::ofstream ofs(filename);
  boost::archive::text_oarchive oa(ofs);
  oa << s;
}

void restore_schedule(bus_schedule &s, const char *filename) {
  // open the archive
  std::ifstream ifs(filename);
  boost::archive::text_iarchive ia(ifs);

  // restore the schedule from the archive
  ia >> s;
}

/* |--bus_schedule: std::list<std::pair<trip_info, bus_route *> >
       |--bus_route: std::list<bus_stop_pointer>
           |--bus_stop: several types of stop
               |--bus_stop_corner
               |--bus_stop_destination
                   |--gps_position: int degrees; int minutes; float seconds;
*/

int main(int argc, char *argv[]) {
  // make the schedule
  bus_schedule original_schedule;

  // fill in the data
  // make a few stops
  bus_stop *bs0 = new bus_stop_corner(gps_position(34, 135, 52.560f),
                                      gps_position(134, 22, 78.30f),
                                      "24th Street", "10th Avenue");
  bus_stop *bs1 = new bus_stop_corner(gps_position(35, 137, 23.456f),
                                      gps_position(133, 35, 54.12f),
                                      "State street", "Cathedral Vista Lane");
  bus_stop *bs2 =
      new bus_stop_destination(gps_position(35, 136, 15.456f),
                               gps_position(133, 32, 15.300f), "White House");
  bus_stop *bs3 = new bus_stop_destination(gps_position(35, 134, 48.789f),
                                           gps_position(133, 32, 16.230f),
                                           "Lincoln Memorial");

  // make a  routes
  bus_route route0;
  route0.append(bs0);
  route0.append(bs1);
  route0.append(bs2);

  // add trips to schedule
  original_schedule.append("bob", 6, 24, &route0);
  original_schedule.append("bob", 9, 57, &route0);
  original_schedule.append("alice", 11, 02, &route0);

  // make aother routes
  bus_route route1;
  route1.append(bs3);
  route1.append(bs2);
  route1.append(bs1);

  // add trips to schedule
  original_schedule.append("ted", 7, 17, &route1);
  original_schedule.append("ted", 9, 38, &route1);
  original_schedule.append("alice", 11, 47, &route1);

  // display the complete schedule
  std::cout << "original schedule";
  std::cout << original_schedule;

  std::string filename(boost::archive::tmpdir());
  filename += "/demofile.txt";

  // save the schedule
  save_schedule(original_schedule, filename.c_str());

  // ... some time later
  // make  a new schedule
  bus_schedule new_schedule;

  restore_schedule(new_schedule, filename.c_str());

  // and display
  std::cout << "\nrestored schedule";
  std::cout << new_schedule;
  // should be the same as the old one. (except for the pointer values)

  delete bs0;
  delete bs1;
  delete bs2;
  delete bs3;
  return 0;
}

若是编译报错,
相关的库函数没有定义undefined reference to boost::archive::text_oarchive之类的
需要在后面加上相应的库例如:

-lboost_serialization -lboost_system

或者是修改CMakeLists.txt中的内容

cmake_minimum_required(VERSION 3.10)
project(boostTest)
find_package(Boost
    1.71.0
    COMPONENTS serialization
    REQUIRED)

add_executable(main main.cpp)

target_link_libraries(main Boost::serialization)

以上. 2023.03.30

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值