Boost.Serialization

libraryserializationC++Boostcomprehensivecross-platform

Library

Boost.Serialization

Overview

Boost.Serialization is a comprehensive serialization library that makes it possible to convert objects in a C++ program to a sequence of bytes that can be saved and loaded to restore the objects. It enables the reversible deconstruction of arbitrary C++ data structures to a sequence of bytes, which can be used to reconstitute an equivalent structure in another program context. This can be used to implement object persistence, remote parameter passing, or other serialization-required facilities.

Details

As part of the Boost library collection, Boost.Serialization provides industrial-standard quality and reliability. It offers all the features needed for enterprise applications, including non-intrusive serialization, automatic pointer tracking, comprehensive version management, and support for multiple archive formats.

Key features:

  • Multiple Archive Formats: Supports text, binary, and XML archives
  • Non-Intrusive Design: Can serialize existing classes without modification
  • Automatic Pointer Handling: Automatic tracking of pointers and handling of circular references
  • Complete STL Support: Built-in support for all STL containers
  • Versioning: Independent version management for each class
  • Polymorphism Support: Serialization of derived classes through base class pointers
  • Cross-Platform: Works on 32/64-bit, Windows, Linux, Solaris, etc.
  • Thread Safety: Concurrent reading/writing of different archive instances is permitted
  • Array Optimization: Optimizations for contiguous arrays of homogeneous data

Archive interface:

  • All archive class interfaces are identical
  • Once serialization is defined for a class, it can be serialized to any archive type
  • Easy creation of new archive types
  • Rich interface allows presenting serialized data as XML in a useful manner

Pros and Cons

Pros

  • Industrial Standard Quality: High quality and reliability as a Boost library
  • Comprehensive Features: Complete feature set for enterprise-level requirements
  • Maturity: Years of proven stability and extensive documentation
  • Non-Intrusive: No need to modify existing classes, including library classes
  • Automatic Pointer Handling: Safe serialization of complex data structures
  • Excellent Extensibility: Easy to create custom archives and serializers
  • Large Community: Abundant information and support
  • Standards Influence: Track record of contributing to C++ standardization

Cons

  • Learning Curve: Time needed for complete understanding due to rich features
  • Compile Time: Increased compile times due to heavy template usage
  • Binary Size: Large linked library size
  • Performance: May have slower processing speed compared to some lightweight libraries
  • Dependencies: Dependency on the entire Boost library (partial use possible)
  • Modern C++ Support: Conservative adoption of C++11 and later features

References

Code Examples

Basic Serialization

#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() {
    // Save object
    {
        Person person("John Doe", 30);
        std::ofstream ofs("person.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << person;
    }
    
    // Load object
    {
        Person person;
        std::ifstream ifs("person.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> person;
        person.print();
    }
    
    return 0;
}

STL Container Serialization

#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("Tech Corp");
    company.addEmployee({"Alice", "Development", 75000});
    company.addEmployee({"Bob", "Sales", 65000});
    company.addEmployee({"Charlie", "Development", 80000});
    
    // Save with binary archive
    {
        std::ofstream ofs("company.dat", std::ios::binary);
        boost::archive::binary_oarchive oa(ofs);
        oa << company;
    }
    
    // Load
    Company loaded_company;
    {
        std::ifstream ifs("company.dat", std::ios::binary);
        boost::archive::binary_iarchive ia(ifs);
        ia >> loaded_company;
    }
    
    return 0;
}

Pointers and Polymorphism

#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) {
        // Base class processing
    }
};

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;
    }
};

// Register derived classes
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));
    
    // Save with XML archive
    {
        std::ofstream ofs("shapes.xml");
        boost::archive::xml_oarchive oa(ofs);
        oa << BOOST_SERIALIZATION_NVP(shapes);
    }
    
    // Load
    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;
}

Versioning

#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;      // Added in version 1
    int revision_count;      // Added in version 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) {}
};

// Specify class version
BOOST_CLASS_VERSION(Document, 2)

int main() {
    Document doc("Technical Document", "About Boost.Serialization usage...");
    
    // Save
    {
        std::ofstream ofs("document.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << doc;
    }
    
    // Can load files from different versions
    Document loaded_doc;
    {
        std::ifstream ifs("document.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> loaded_doc;
    }
    
    return 0;
}

Non-Intrusive Serialization

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

// Third-party class (cannot be modified)
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; }
};

// Non-intrusive serialization functions
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);
}

}
}

// Specify save/load split
BOOST_SERIALIZATION_SPLIT_FREE(ThirdPartyClass)

int main() {
    ThirdPartyClass obj(42);
    
    // Save
    {
        std::ofstream ofs("thirdparty.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << obj;
    }
    
    // Load
    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;
}

Using Custom Archives

#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;
    }
};

// Stream-based serialization
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"}};
    
    // Serialize to string (can be used for network transmission)
    std::string serialized = serializeToString(config);
    std::cout << "Serialized: " << serialized << std::endl;
    
    // Deserialize from string
    Configuration restored = deserializeFromString(serialized);
    
    return 0;
}