原文翻译自:
http://uscilab.github.io/cereal/transition_from_boost.html
如果你曾经使用过Boost序列化功能,你会发现Cereal和Boost很相似。这是因为Cereal被设计时就考虑了Boost用户的使用习惯,模仿了许多Boost序列化库的语法习惯。本文是一个简要的过渡指南。请保证你已经能正常安装Cereal,并且对基本语法有一个简要的认识,可参考文章“Cereal快速入门”。
TLDR版本(TLDR??)
Cereal和Boost序列库的接口非常相似,在一些情况下可以非常迅速的将Boost库替换成Cereal。但是即便如此,Cereal和Boost还是有很大的区别的,想要了解更多继续阅读本文或者参考
http://uscilab.github.io/cereal/assets/doxygen/index.html。
差异
- Cereal在序列化时只存储非常少量的元数据(metadata)。反之Boost序列化库存储了大量的元数据,例如Boost库版本等等。Cereal则并不需要版本信息来保证序列化和反序列化时库版本一致。
- Cereal只需要头文件即可,不再需要其他依赖库。在使用Boost时,由于Boost库繁多复杂,一个非常头疼的问题是如何保证不同机器间的Boost版本一致。但是这个问题在使用Cereal时并不存在,因为Cereal非常容易安装和使用。
- Cereal基本支持所有的标准库。并且一些Cereal支持的标准,Boost并不支持,这包括:forward_list、memory、queue、stack、tuple、unordered_set和unordered_map。
- Cereal不支持指针,并且需要支持C++11的编译器。但是从一个使用者的角度来看,Cereal的代码是非常简单理解和扩展的。
- 相比Boost,Cereal更加简洁。例如,在Cereal中当把serialize函数分成load/save函数时,不需要提前使用宏声明。Cereal还使用了static_assert,提供了更加准确的错误提示。
- Cereal和Boost使用了不同的语法规则进行序列化。Boost使用的是&,<<和>>,而在Cereal中使用的是(),例如archive(myData1,myData2)。但是为了方便,Cereal也支持Boost的语法规则。总而言之Cereal和Boost非常相似,但是读者在使用时还说要根据情况认真阅读doxygen文档以分辨其中的细小差异。
过渡的例子
要将Boost修改成Cereal,并不需要对代码进行大量修改。前文中已经提到,Cereal支持Boost的语法规则。下文中给出一个例子,示例如何从Boost转移到Cereal中。
#include <boost/archive/binary_oarchive.hpp>
#include <cereal/archives/binary.hpp>
#include <fstream>
class SomeData
{
public:
SomeData() = default;
int a;
int b;
private:
friend class cereal::access; // 友函数,获取版本信息
boost::serialization::access;
// 可选项,Cereal支持版本信息
//
// 注意第二个参数从const unigned int改成了const std::uint32_t
template <class Archive>
void save( Archive & ar, std::uint32_t const version ) const
{
// Cereal支持Boost语法
ar << a << b;
}
template <class Archive>
void load( Archive & ar, std::uint32_t const version )
{
ar >> a;
ar >> b;
}
// Cereal并不需要此处声明
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
//当使用Cereal时,由于BOOST_SERIALIZATION_SPLIT_MEMBER由于声明了一个serialize函数,会使得Cereal产生一个错误。Cereal不能同时支持load/save和serialize。
// 为解决此问题,需要临时使用一个特殊函数来保证Cereal不把Boost的宏当做错误。当你把这个宏删掉后,这个特殊函数也就没有必要再使用了。
CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES( SomeData, cereal::specialization::member_load_save )
BOOST_CLASS_VERSION(SomeData, 1);
CEREAL_CLASS_VERSION(SomeData, 1);
struct MyType
{
int x;
double y;
SomeData s;
template <class Archive>
void serialize( Archive & ar, std::uint32_t const version )
{
ar & x & y; // 在Cereal中&是合法的,但并不推荐
ar & s;
}
};
int main()
{
std::ofstream os("out.bin", std::ios::binary);
// using boost
{
boost::archive::binary_oarchive ar(os);
MyType m;
ar << m; // Cereal支持Boost的语法
}
// using cereal
{
cereal::BinaryOutputArchive ar(os);
MyType m;
ar << m;
}
return 0;
}
在上述代码中,仅仅通过添加一个Cereal版本友函数,就能将Boost快速过渡到Cereal。但是当你使用Boost中的其他特性时,可能需要修改其他代码,不再赘述。
观察代码不难发现,在Boost使用load/save模式序列化,需要使用宏BOOST_MEMBER_SPLIT。但是Cereal则更人性化,直接使用即可。
不能将boost::serialization::access设置为友函数,应该将cereal::access设置为友函数,详情参照cereal/access.hpp。
在Cereal,类版本信息是一个额外信息,不是必要信息,因此在serialization函数通常将其作为第二个参数,格式为std::uint32_t const。除此之外,宏BOOST_CLASS_VERSION 在Cereal中有一个对应宏CEREAL_CLASS_VERSION。
总结,Cereal和Boost有非常多的相似之处,但是也略有差异。因此特别建议在使用时详细阅读doxygen文档。
下文把上文重写,改成了完全的Cereal格式:
#include <cereal/archives/binary.hpp>
#include <fstream>
class SomeData
{
public:
SomeData() = default;
int a;
int b;
private:
friend class cereal::access;
// 版本信息是可选项
template <class Archive>
void save( Archive & ar, std::uint32_t const version ) const
{
ar( a, b );
}
template <class Archive>
void load( Archive & ar, std::uint32_t const version )
{
ar( a, b );
}
};
CEREAL_CLASS_VERSION(SomeData, 1);
struct MyType
{
int x;
double y;
SomeData s;
template <class Archive>
void serialize( Archive & ar, std::uint32_t const version )
{
ar( x, y );
ar( s );
}
};
int main()
{
std::ofstream os("out.bin", std::ios::binary);
{
cereal::BinaryOutputArchive ar(os);
MyType m;
ar( m );
}
return 0;
}