Archive
An archive is a sequence of bytes that represented serialized C++ objects. Objects can be added to an archive to serialize them and then later loaded from the archive.
1. boost::archive::text_iarchive
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <fstream> using namespace boost::archive; void save() { std::ofstream file("archive.txt"); text_oarchive oa{file}; int i = 1; oa << i; } void load() { std::ifstream file("archive.txt"); text_iarchive ia{file}; int i = 0; ia >> i; std::cout << i << std::endl; } int main() { save(); load(); return 0; }
The class boost::archive::text_oarchive serializes data as a text stream, and the class boost::archive::text_iarchive restores data from such a text stream. Constructors of archives expect an input or output stream as a parameter. The stream is used to serialize or restore data.
2. serializing with a stringstream
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; void save() { text_oarchive oa{ss}; int i = 1; oa << i; } void load() { text_iarchive ia{ss}; int i = 0; ia >> i; std::cout << i << std::endl; } int main() { save(); load(); return 0; }
output: 1
3. user-defined types with a member function
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} int legs() const { return legs_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; void save() { text_oarchive oa(ss); animal a{4}; oa << a; } void load() { text_iarchive ia(ss); animal a; ia >> a; std::cout << a.legs() << std::endl; } int main() { save(); load(); return 0; }
In order to serialize objects of user-defined types, you must define the member function serialize(). This function is called when the object is serialized to or restored from a byte stream.
serialize() is automatically called any time an object is serialized or restored.
4. serializing with a free-standing function and serializing strings
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/string.hpp> #include <iostream> #include <sstream> #include <string> #include <utility> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs, std::string name) : legs_{legs}, name_{std::move(name)} {} int legs() const { return legs_; } const std::string &name() const { return name_; } private: friend class boost::serialization::access; template <typename Archive> friend void serialize(Archive &ar, animal &a, const unsigned int version); int legs_; std::string name_; }; template <typename Archive> void serialize(Archive &ar, animal &a, const unsigned int version) { ar & a.legs_; ar & a.name_; } void save() { text_oarchive oa{ss}; animal a{4, "cat"}; oa << a; } void load() { text_iarchive ia{ss}; animal a; ia >> a; std::cout << a.legs() << std::endl; std::cout << a.name() << std::endl; } int main() { save(); load(); return 0; }
a member variable of type std::string, in order to serialize this member variable. the header file boost/serialization/string.hpp must be included to provide the appropriate free-standing function serialize().
Pointers and References
1. serializing pointers
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <sstream> std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_{legs} {} int legs() const { return legs_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; void save() { boost::archive::text_oarchive oa(ss); animal *a = new animal(4); oa << a; std::cout << std::hex << a << std::endl; delete a; } void load() { boost::archive::text_iarchive ia(ss); animal *a; ia >> a; std::cout << std::hex << a << std::endl; std::cout << std::dec << a->legs() << std::endl; delete a; } int main() { save(); load(); return 0; }
Boost.Serialization automatically serializes the object referenced by a and not the address of the object.
If the archive is restored, a will not necessarily contain the same address. A new object is created and its address is assigned to a instead. Boost.Serialization only guarantees that the object is the same as the one serialized, not that its address is the same.
2 serializing smart pointers
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} int legs() const { return legs_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; void save() { text_oarchive oa(ss); boost::scoped_ptr<animal> a(new animal(4)); oa << a; } void load() { text_iarchive ia(ss); boost::scoped_ptr<animal> a; ia >> a; std::cout << a->legs() << std::endl; } int main() { save(); load(); return 0; }
uses the smart pointer boost::scoped_ptr
to manage a dynamically allocated object of type animal
. Include the header file boost/serialization/scoped_ptr.hpp
to serialize such a pointer. To serialize a smart pointer of type boost::shared_ptr
, use the header file boost/serialization/shared_ptr.hpp
.
3. serializing references
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} int legs() const { return legs_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; void save() { text_oarchive oa(ss); animal a(4); animal &r = a; oa << r; } void load() { text_iarchive ia(ss); animal a; animal &r = a; ia >> r; std::cout << r.legs() << std::endl; } int main() { save(); load(); }
Serialization of Class Hierarchy Objects
Derived classes must access the function boost::serialization::base_object() inside the member function serialize() to serialize objects based on class hierarchies. This function guarantees that inherited member variables of base classes are correctly serialized.
1. serializing derived classes correctly
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} int legs() const { return legs_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; class bird : public animal { public: bird() = default; bird(int legs, bool can_fly) : animal(legs), can_fly_{can_fly} {} bool can_fly() const { return can_fly_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & boost::serialization::base_object<animal>(*this); ar & can_fly_; } bool can_fly_; }; void save() { text_oarchive oa(ss); bird penguin(2, false); oa << penguin; } void load() { text_iarchive ia(ss); bird penguin; ia >> penguin; std::cout << penguin.legs() << '\n'; std::cout << std::boolalpha << penguin.can_fly() << '\n'; } int main() { save(); load(); return 0; }
Inherited member variables are serialized by accessing the base class inside the member function serialize()
of the derived class and calling boost::serialization::base_object()
. You must use this function rather than, for example, static_cast
because only boost::serialization::base_object()
ensures correct serialization
2. registering derived classes statically with BOOST_CLASS_EXPORT
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/export.hpp> #include <iostream> #include <sstream> using namespace boost::archive; std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} virtual int legs() const { return legs_; } virtual ~animal() = default; private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; class bird : public animal { public: bird() = default; bird(int legs, bool can_fly) : animal{legs}, can_fly_(can_fly) {} bool can_fly() const { return can_fly_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & boost::serialization::base_object<animal>(*this); ar & can_fly_; } bool can_fly_; }; BOOST_CLASS_EXPORT(bird) void save() { text_oarchive oa(ss); animal *a = new bird(2, false); oa << a; delete a; } void load() { text_iarchive ia(ss); animal *a; ia >> a; std::cout << a->legs() << '\n'; delete a; } int main() { save(); load(); return 0; }
To have Boost.Serialization recognize that an object of type bird
must be serialized, even though the pointer is of type animal*
, the class bird
needs to be declared. This is done using the macro BOOST_CLASS_EXPORT
, which is defined in boost/serialization/export.hpp
. Because the type bird
does not appear in the pointer definition, Boost.Serialization cannot serialize an object of type bird
correctly without the macro.
The macro BOOST_CLASS_EXPORT
must be used if objects of derived classes are to be serialized using a pointer to their corresponding base class. A disadvantage of BOOST_CLASS_EXPORT
is that, because of static registration, classes can be registered that may not be used for serialization at all.
3. register_type()
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/export.hpp> #include <iostream> #include <sstream> std::stringstream ss; class animal { public: animal() = default; animal(int legs) : legs_(legs) {} virtual int legs() const { return legs_; } virtual ~animal() = default; private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & legs_; } int legs_; }; class bird : public animal { public: bird() = default; bird(int legs, bool can_fly) : animal{legs}, can_fly_(can_fly) {} bool can_fly() const { return can_fly_; } private: friend class boost::serialization::access; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & boost::serialization::base_object<animal>(*this); ar & can_fly_; } bool can_fly_; }; void save() { boost::archive::text_oarchive oa(ss); oa.register_type<bird>(); animal *a = new bird(2, false); oa << a; delete a; } void load() { boost::archive::text_iarchive ia(ss); ia.register_type<bird>(); animal *a; ia >> a; std::cout << a->legs() << std::endl; delete a; } int main() { save(); load(); return 0; }
The type to be registered is passed as a template parameter. Note that register_type()
must be called both in save()
and load()
.
The advantage of register_type()
is that only classes used for serialization must be registered. For example, when developing a library, one does not know which classes a developer may use for serialization later. While the macro BOOST_CLASS_EXPORT
makes this easy, it may register types that are not going to be used for serialization.