Introduction
As developers,we often want to save data from an object into a file with an XML based data format. The principle of loose coupling suggests that each object shouldn't produce its own XML code directly; there's no need for every class to know about XML. Even using an abstract representation of the XML format,such as a DOM tree,requires the class to know too much about XML. Also,writing XML is unnecessary low-level work for the developer. It's better to save to a simple,completely abstract format,and move elsewhere the details of how to save that format to XML. The Boost Serialization and Archive libraries allow this.
Serialization
To add serialization support to a class,you write a serialize() method that describes in what order the data fits into a generic archive. Here's an example:
#include <list> #include <string> #include <boost/serialization/list.hpp> #include <boost/serialization/string.hpp> #include <boost/serialization/nvp.hpp> class italian_sandwich { public: italian_sandwich(); private: string m_bread,m_cheese; list<string> m_meats; bool m_spicy_eggplant_p; friend class boost::serialization::access; template<class archive> void serialize(archive& ar,const unsigned int version) { using boost::serialization::make_nvp; ar & make_nvp("Bread",m_bread); ar & make_nvp("Cheese",m_cheese); ar & make_nvp("Meats",m_meats); ar & make_nvp("Add Spicy Eggplant",m_spicy_eggplant_p); } }
Notes:
- The & operator becomes << when saving and >> when loading.
- make_nvp() assigns a name to each element we want to save.
- The unused version argument could be used later if we change the class.
Note how we didn't need to manually descend into the list of strings (m_meats) and serialize each string individually. As long as a type is serializable,STL containers of that type are serialized automatically. Similarly,if we built serialization support for one of our own classes,say a bread_t class,then we could still serialize the m_bread attribute with the same simple code,instead of manually descending into the m_bread object.
Archive
To save/load a serializable object to/from an XML file,we create a file stream,initialize an XML archive with that stream,and use the << or >> operator to write the object out to the archive or read it in. Here's an example using italian_sandwich:
#include <base/file_stream.hpp> #include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> #include <boost/serialization/nvp.hpp> void save_sandwich(const italian_sandwich& sw,const string& file_name) { typedef base::file_stream bafst; bafst::file_stream ofs(file_name,bafst::trunc | bafst::out); boost::archive::xml_oarchive xml(ofs); xml << boost::serialization::make_nvp("Italian Sandwich",sw); } italian_sandwich load_sandwich(const string& file_name) { typedef base::file_stream bafst; italian_sandwich sw; bafst::file_stream ifs(file_name,bafst::binary | bafst::in); boost::archive::xml_oarchive xml(ifs); xml >> boost::serialization::make_nvp("Italian Sandwich",sw); }
Notes:
- Exception handling code is needed around the file_stream and archive initialization.
- make_nvp() is not strictly necessary.
Further Examples
italian_sandwich was a very simple example class. It is easy to handle more complicated cases,for example:
- If we can't modify a class directly,we can add a free serialization function outside it that uses only public methods. Its signature would look like:
template<class archive> void serialize(archive& ar,italian_sandwich& sw,const unsigned int version);
If the class has a method to save/load a data attribute (eg. for safety-checking),then the same code won't work for loading or saving to the archive. In that case we can split the serialize() method into save() and load():#include "bread_t.hpp" #include <boost/serialization/nvp.hpp> #include <boost/serialization/split_member.hpp> template<class archive> void load(archive& ar,const unsigned int version) { bread_t bread; ar & boost::serialization::make_nvp("Bread",bread); set_bread(bread); // load other ingredients... } template<class archive> void save(archive& ar,const unsigned int version) { const bread_t bread = get_bread(); ar & boost::serialization::make_nvp("Bread",bread); // save other ingredients... } // define serialize() using save() and load() BOOST_SERIALIZATION_SPLIT_MEMBER();
There are other archive formats,such as human readable text or binary formats. Once a class has serialization support,we can use any of those archive formats without changing the class.Conclusion
Hopefully these two Boost libraries can prevent us from writing some unnecessary code.@H_502_76@@H_502_76@For further reference,see the@H_502_76@Boost documentation page.