Boost.Serialization

ライブラリシリアライゼーションC++Boost包括的クロスプラットフォーム

ライブラリ

Boost.Serialization

概要

Boost.Serializationは、C++プログラムのオブジェクトをバイトシーケンスに変換し、保存・復元を可能にする包括的なシリアライゼーションライブラリです。任意のC++データ構造を可逆的にバイトシーケンスに分解し、別のプログラムコンテキストで同等の構造を再構築できます。オブジェクトの永続化、リモートパラメータパッシング、その他のシリアライゼーションが必要な機能の実装に使用されます。

詳細

Boost.Serializationは、Boostライブラリコレクションの一部として、産業標準レベルの品質と信頼性を提供します。非侵入的なシリアライゼーション、自動的なポインタ追跡、包括的なバージョン管理、複数のアーカイブ形式のサポートなど、エンタープライズアプリケーションに必要なすべての機能を備えています。

主な特徴:

  • 複数のアーカイブ形式: テキスト、バイナリ、XMLアーカイブをサポート
  • 非侵入的設計: 既存クラスの変更不要でシリアライゼーション可能
  • 自動ポインタ処理: ポインタの追跡と循環参照の処理を自動化
  • 完全なSTLサポート: すべてのSTLコンテナに対する組み込みサポート
  • バージョニング: クラスごとの独立したバージョン管理
  • ポリモーフィズムサポート: 基底クラスポインタ経由での派生クラスのシリアライゼーション
  • クロスプラットフォーム: 32/64ビット、Windows、Linux、Solaris等で動作
  • スレッドセーフ: 異なるアーカイブインスタンスの同時読み書きが可能
  • 配列最適化: 同種データの連続配列に対する最適化

アーカイブインターフェース:

  • すべてのアーカイブクラスのインターフェースは同一
  • 一度シリアライゼーションを定義すれば、任意のアーカイブタイプで使用可能
  • 新しいアーカイブタイプの作成が容易
  • XMLとして有用な形式でシリアライズデータを表現可能

メリット・デメリット

メリット

  • 産業標準の品質: Boostライブラリとしての高い品質と信頼性
  • 包括的な機能: エンタープライズレベルの要求に対応する完全な機能セット
  • 成熟度: 長年の実績と安定性、豊富なドキュメント
  • 非侵入的: ライブラリクラスを含む既存クラスの変更不要
  • ポインタの自動処理: 複雑なデータ構造も安全にシリアライズ
  • 優れた拡張性: カスタムアーカイブやシリアライザの作成が容易
  • 大規模コミュニティ: 豊富な情報とサポート
  • 標準化への影響: C++標準化への貢献実績

デメリット

  • 学習曲線: 豊富な機能ゆえに、完全な理解には時間が必要
  • コンパイル時間: テンプレートの多用によりコンパイル時間が増加
  • バイナリサイズ: リンクされるライブラリのサイズが大きい
  • パフォーマンス: 一部の軽量ライブラリと比較して処理速度が劣る場合がある
  • 依存関係: Boostライブラリ全体への依存(部分的な使用も可能)
  • モダンC++対応: C++11以降の機能への対応が保守的

参考ページ

書き方の例

基本的なシリアライゼーション

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

class Person {
private:
    friend class boost::serialization::access;
    std::string name;
    int age;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & name;
        ar & age;
    }
    
public:
    Person() {}
    Person(const std::string& n, int a) : name(n), age(a) {}
    
    void print() const {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }
};

int main() {
    // オブジェクトの保存
    {
        Person person("山田太郎", 30);
        std::ofstream ofs("person.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << person;
    }
    
    // オブジェクトの読み込み
    {
        Person person;
        std::ifstream ifs("person.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> person;
        person.print();
    }
    
    return 0;
}

STLコンテナのシリアライゼーション

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/string.hpp>

struct Employee {
    std::string name;
    std::string department;
    double salary;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & name & department & salary;
    }
};

class Company {
private:
    friend class boost::serialization::access;
    std::string company_name;
    std::vector<Employee> employees;
    std::map<std::string, int> department_count;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & company_name;
        ar & employees;
        ar & department_count;
    }
    
public:
    Company() {}
    Company(const std::string& name) : company_name(name) {}
    
    void addEmployee(const Employee& emp) {
        employees.push_back(emp);
        department_count[emp.department]++;
    }
};

int main() {
    Company company("テック株式会社");
    company.addEmployee({"佐藤", "開発部", 500000});
    company.addEmployee({"鈴木", "営業部", 450000});
    company.addEmployee({"田中", "開発部", 550000});
    
    // バイナリアーカイブで保存
    {
        std::ofstream ofs("company.dat", std::ios::binary);
        boost::archive::binary_oarchive oa(ofs);
        oa << company;
    }
    
    // 読み込み
    Company loaded_company;
    {
        std::ifstream ifs("company.dat", std::ios::binary);
        boost::archive::binary_iarchive ia(ifs);
        ia >> loaded_company;
    }
    
    return 0;
}

ポインタとポリモーフィズム

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>

class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;
    
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        // 基底クラスの処理
    }
};

class Circle : public Shape {
private:
    friend class boost::serialization::access;
    double radius;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & boost::serialization::base_object<Shape>(*this);
        ar & BOOST_SERIALIZATION_NVP(radius);
    }
    
public:
    Circle() : radius(0) {}
    Circle(double r) : radius(r) {}
    
    double area() const override {
        return 3.14159 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    friend class boost::serialization::access;
    double width, height;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & boost::serialization::base_object<Shape>(*this);
        ar & BOOST_SERIALIZATION_NVP(width);
        ar & BOOST_SERIALIZATION_NVP(height);
    }
    
public:
    Rectangle() : width(0), height(0) {}
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double area() const override {
        return width * height;
    }
};

// 派生クラスの登録
BOOST_CLASS_EXPORT(Circle)
BOOST_CLASS_EXPORT(Rectangle)

int main() {
    std::vector<std::shared_ptr<Shape>> shapes;
    shapes.push_back(std::make_shared<Circle>(5.0));
    shapes.push_back(std::make_shared<Rectangle>(4.0, 6.0));
    
    // XMLアーカイブで保存
    {
        std::ofstream ofs("shapes.xml");
        boost::archive::xml_oarchive oa(ofs);
        oa << BOOST_SERIALIZATION_NVP(shapes);
    }
    
    // 読み込み
    std::vector<std::shared_ptr<Shape>> loaded_shapes;
    {
        std::ifstream ifs("shapes.xml");
        boost::archive::xml_iarchive ia(ifs);
        ia >> BOOST_SERIALIZATION_NVP(loaded_shapes);
    }
    
    for (const auto& shape : loaded_shapes) {
        std::cout << "Area: " << shape->area() << std::endl;
    }
    
    return 0;
}

バージョニング

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

class Document {
private:
    friend class boost::serialization::access;
    std::string title;
    std::string content;
    std::string author;      // バージョン1で追加
    int revision_count;      // バージョン2で追加
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & title;
        ar & content;
        
        if (version >= 1) {
            ar & author;
        }
        
        if (version >= 2) {
            ar & revision_count;
        }
    }
    
public:
    Document() : revision_count(0) {}
    Document(const std::string& t, const std::string& c) 
        : title(t), content(c), author("Unknown"), revision_count(1) {}
};

// クラスのバージョンを指定
BOOST_CLASS_VERSION(Document, 2)

int main() {
    Document doc("技術文書", "Boost.Serializationの使い方について...");
    
    // 保存
    {
        std::ofstream ofs("document.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << doc;
    }
    
    // 異なるバージョンのファイルも読み込み可能
    Document loaded_doc;
    {
        std::ifstream ifs("document.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> loaded_doc;
    }
    
    return 0;
}

非侵入的シリアライゼーション

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

// サードパーティのクラス(変更不可)
class ThirdPartyClass {
    int private_data;
public:
    ThirdPartyClass() : private_data(0) {}
    ThirdPartyClass(int data) : private_data(data) {}
    int getData() const { return private_data; }
    void setData(int data) { private_data = data; }
};

// 非侵入的シリアライゼーション関数
namespace boost {
namespace serialization {

template<class Archive>
void save(Archive & ar, const ThirdPartyClass & obj, const unsigned int version) {
    int data = obj.getData();
    ar & data;
}

template<class Archive>
void load(Archive & ar, ThirdPartyClass & obj, const unsigned int version) {
    int data;
    ar & data;
    obj.setData(data);
}

}
}

// save/loadの分割を指定
BOOST_SERIALIZATION_SPLIT_FREE(ThirdPartyClass)

int main() {
    ThirdPartyClass obj(42);
    
    // 保存
    {
        std::ofstream ofs("thirdparty.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << obj;
    }
    
    // 読み込み
    ThirdPartyClass loaded_obj;
    {
        std::ifstream ifs("thirdparty.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> loaded_obj;
    }
    
    std::cout << "Loaded data: " << loaded_obj.getData() << std::endl;
    
    return 0;
}

カスタムアーカイブの使用

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <sstream>

struct Configuration {
    std::string app_name;
    int version;
    std::vector<std::string> modules;
    
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version) {
        ar & app_name & version & modules;
    }
};

// ストリームベースのシリアライゼーション
std::string serializeToString(const Configuration& config) {
    std::ostringstream oss;
    boost::archive::text_oarchive oa(oss);
    oa << config;
    return oss.str();
}

Configuration deserializeFromString(const std::string& data) {
    std::istringstream iss(data);
    boost::archive::text_iarchive ia(iss);
    Configuration config;
    ia >> config;
    return config;
}

int main() {
    Configuration config{"MyApp", 1, {"Core", "UI", "Network"}};
    
    // 文字列にシリアライズ(ネットワーク送信などに利用可能)
    std::string serialized = serializeToString(config);
    std::cout << "Serialized: " << serialized << std::endl;
    
    // 文字列からデシリアライズ
    Configuration restored = deserializeFromString(serialized);
    
    return 0;
}