C++ 串行化——A practical guide to C++ serialization

为什么需要 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 >> 操作。

如果用户自定义类型的成员变量不是简单类型,序列化和反序列化过程一样,需要区分,可以按照如下定义 saveload 以及 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:

  1. https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_unpacker
  2. http://www.thomaswhitton.com/blog/2013/07/03/binary-message-format-c-plus-plus-examples/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值