为什么需要 serialization ? 因为有时候需要将一个结构化的数据传输到网络上,有时候需要将数据保存成文本;然后其他程序在需要使用这个数据的时候直接从网络接受或者从文本读取然后恢复成原本的数据结构即可。
1.Boost serialization
Boost 有很多种 archive 格式,例如 Text, XML, and Binary archives,但是基本上只用 Text 和 XML,因为 Binary 的不保证 32bit/64bit 大端/小端等平台兼容性。
这里只介绍 Non Intrusive Version,也就是不需要修改原来的类定义:。
show me the code, 一个简单例子:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class gps_position
{
public:
int degrees;
int minutes;
float seconds;
gps_position(){};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
};
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive & ar, gps_position & g, const unsigned int version)
{
ar & g.degrees;
ar & g.minutes;
ar & g.seconds;
}
} // namespace serialization
} // namespace boost
operator &
调用会根据这个 archive 是 oarchive 还是 iarchive 调用 operator <<
操作或 operator >>
操作。
如果用户自定义类型的成员变量不是简单类型,序列化和反序列化过程不一样,需要区分,可以按照如下定义 save
和 load
以及 serialize
三个函数:
#include <boost/serialization/split_free.hpp>
template<class Archive>
inline void serialize(Archive & ar, MyClass & d, const unsigned int file_version)
{
split_free(ar, d, file_version); // 或者使用宏:BOOST_SERIALIZATION_SPLIT_FREE(MyClass )
}
template<class Archive>
void save(Archive & ar, const MyClass & d, unsigned int version)
{
// save for MyClass
ar & d.xxx; // 同样也可以使用 ar << d.xxx
ar & d.yyy;
}
template<class Archive>
void load(Archive & ar, MyClass & d, unsigned int version)
{
// load for MyClass
ar & d.xxx; // 同样也可以使用 ar >> d.xxx
ar & d.yyy;
}
boost 针对 STL 容器实现了一套序列化的操作,前提是容器中的类型支持 boost 序列化操作。需要序列化相应容器,只需要包含对应的头文件即可:
#include <boost/serialization/array.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/hash_map.hpp>
#include <boost/serialization/hash_set.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/slist.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/bitset.hpp>
#include <boost/serialization/string.hpp>
https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/tutorial.html
http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/
2. Msgpack serialization
同样 msgpack 也支持 Boost serialization 的功能,而且 msgpack 更高效更紧凑。更重要的是, msgpack c++ 库只需要包含头文件即可,不需要依赖库(但是 msgpack 不支持持久化 archive,用户需要自己将串行化的数据写入文件)。
同样,以上文中的类 gps_position
为例:
#include "msgpack.hpp"
#include <sstream>
class gps_position
{
public:
int degrees;
int minutes;
float seconds;
gps_position(){};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
// MSGPACK_DEFINE(degrees, minutes, seconds); // Intrusive
};
int main()
{
gps_position pos(1, 2, 1.23);
std::stringstream ss;
msgpack::pack(ss, pos.degrees); // pack
msgpack::pack(ss, pos.minutes);
msgpack::pack(ss, pos.seconds);
std::string str = ss.str();
...
msgpack::object_handle result1; // unpack
msgpack::object_handle result2;
msgpack::object_handle result3;
const char* buffer = str.c_str();
std::size_t len = str.size();
std::size_t offset = 0; // 每次 unpack 会自动更新该值
// Unpack the each msgpack data.
// off is updated when function is returned.
unpack(result1, buffer, len, offset ); // pos.degrees
msgpack::object obj = result1.get(); // 此处返回的是一个引用,因此当 result1 被析构后,该引用便成了悬空引用(dangling reference)
obj.convert(pos.degrees);
unpack(result2, buffer, len, offset ); // pos.minutes
obj = result2.get();
obj.convert(pos.minutes);
unpack(result3, buffer, len, offset ); // pos.seconds
obj = result3.get();
obj.convert(pos.seconds);
assert(offset == len); // unpack结束的标志
}
同样,类似于 Boost 的 load & save
函数,对于自定义类型,msgpack 需要写特定类型的 pack & convert
函数(convert 就是 unpack):
namespace msgpack{
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS){
namespace adaptor
{
// MyClass
template<>
struct convert<MyClass>
{
msgpack::object const& operator()(msgpack::object const& o, MyClass& v) const
{
// 特定类型的 unpack
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
...
return o;
}
};
template<>
struct pack<MyClass>
{
template <typename Stream>
packer<Stream>& operator()(msgpack::packer<Stream>& o, MyClassconst& v) const {
// 特定类型的 pack
return o;
}
};
} // namespace adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack
同样,msgpack 也预先定了各种 STL 容器(vector, map, tuple etc.)的 pack & convert 函数,需要时直接使用即可。
另外,msgpack::object_handle
除了包含一个 msgpack::object
的成员变量外,还包含 msgpack::zone
的成员变量(unique_ptr
),不过 msgpack::zone
一般是针对复杂应用设计的,正常不需要接触 msgpack::zone
的复杂功能。
ref link: