Boost.Serialization可动态配置序列化
Boost.Serialization是一个C++对象序列化库,支持raw指针、STL容器、智能指针等,功能很强大(简介link)。但是实现时大量使用了宏和模板,源码比较复杂,文档不够完善,需要自定义一些东西时比较麻烦。本文中提供了自定义Archive的和动态配置序列化的实现。
假设有需求,希望动态控制其序列化,类似stackoverflow的问题link1和link2。有时候为了效率希望忽略某些字段,有时候又需要序列化这些字段,如下:
class Node { public: Node *m_pNext; std::string m_data; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & m_pNext if ( ... ) // 这里希望动态选择条件 { ar & m_data; } } };
有几种解决思路:
1.使用版本参数。
2. 添加一个全局变量,serialize函数通过该变量判断如何进行序列化。
3. 在Archive中加入一个成员变量,serialize函数使用该变量。
第一种方法是不可行的,因为Boost.Serialization中Version是使用宏BOOST_CLASS_VERSION(ClassName, version)静态设置的,是一个编译期常量。第2种方法可行,但是在多线程环境下需要对该变量加锁,影响效率。我们来尝试第三种方法,构造一个自定义Archive,添加变量with_data来决定是否序列化data:
#include <boost/archive/impl/basic_text_oarchive.ipp> #include <boost/archive/impl/text_oarchive_impl.ipp> #include <boost/archive/impl/archive_pointer_oserializer.ipp> #include <boost/serialization/utility.hpp> #include <boost/serialization/shared_ptr.hpp> #include <iostream> using namespace std; class oarchive_custom: public boost::archive::text_oarchive_impl<oarchive_custom> { public: oarchive_custom(std::ostream & os, unsigned int flags = 0) : with_data(false) , boost::archive::text_oarchive_impl<oarchive_custom>(os, flags) {} ~oarchive_custom(){} bool if_with_data() { return with_data; } bool with_data; }; BOOST_SERIALIZATION_REGISTER_ARCHIVE(oarchive_custom) class Node { public: Node *m_pNext; std::string m_data; template <typename Archive> void serialize(Archive &ar, const unsigned int version) { ar & m_pNext; if (ar.if_with_data()) { ar & m_data; } } }; int main() { Node node; node.m_pNext = NULL; node.m_data = "test_string"; oarchive_custom oa(cout); oa.with_data = false; oa & node; return 0; }
在VisualStudio2008中试了一下,非常可惜,这样是编译不通的,模板展开Archive并不是oarchive_custom,而是text_oarhive_impl<oarchive_custom>(GCC 3.4.5与预期一致,是oarchive_custom ):
直接在Archvie中添加变量行不通,可以另辟蹊径,使用模板静态函数来实现:
class oarchive_with_data: public boost::archive::text_oarchive_impl<oarchive_with_data> { public: oarchive_with_data(std::ostream & os, unsigned int flags = 0) : boost::archive::text_oarchive_impl<oarchive_with_data>(os, flags) {} ~oarchive_with_data(){} static bool if_with_data() { return true; } }; BOOST_SERIALIZATION_REGISTER_ARCHIVE(oarchive_with_data) class oarchive_without_data: public boost::archive::text_oarchive_impl<oarchive_without_data> { public: oarchive_without_data(std::ostream & os, unsigned int flags = 0) : boost::archive::text_oarchive_impl<oarchive_without_data>(os, flags) {} ~oarchive_without_data(){} static bool if_with_data() { return false; } }; BOOST_SERIALIZATION_REGISTER_ARCHIVE(oarchive_without_data)
这里定义两个类,分别对应序列化data和不序列化data,在serialize函数中加入判断(GCC 3.4.5模板展开处理与VS2008不一致,需要特殊处理):
#ifdef __GNUC__ template <class _Archive> void serialize(_Archive& ar, const unsigned int version) #else template <template<class> class ArchiveImpl, class Archive> void serialize(ArchiveImpl<Archive> &ar, const unsigned int version) #endif { ar & m_pNext; if (Archive::if_with_data()) { ar & m_data; } }
序列化时,如果需要携带data信息,使用oarchive_with_data,反之,使用oarchive_without_data:
int main() { Node node; node.m_pNext = NULL; node.m_data = "test_string"; cout << "result of oarchvie_without_ata: "; oarchive_without_data oa1(cout); oa1 & node; cout << endl; cout << "result of oarchvie_with_ata: "; oarchive_with_data oa2(cout); oa2 & node; return 0; }
输出结果:
注意: Boost.Serialization 1.37库有bug(其他版本没有测试),在实现自定义Archvie时需要去掉库自带的定义,例如本文中自定义的 Archive是从text_oarchive_impl派生的,需要如下修改:
# ${/boost_1_37_0}/boost/archive/text_oarchive.hpp 97 // BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::archive::text_oarchive)