简介
github源码地址:
个人注释gitee地址:yas_comment: 对yas源码进行注释https://gitee.com/AriesSun/yas_comment
yas是Yet Another Serialization的简称,又一个序列化的库。yas是基于C++语言开发的header only的序列化的库,支持的归档类型包括二进制、json、文本文件等。
与之类似的序列化库包括:thrift、protobuf、boost、msgpack、cereal、avro、capnproto、flatbuffers等。
特点
- 为替代boost.serialization而创建,因为boost.serialization的序列化速度不够快
- header only library
- does not depend on third-party libraries or boost
- require C++11 support,至少C++ 11
支持的编译器
- GCC : 4.8.5, ... - 32/64 bit
- MinGW: 4.8.5, ... - 32/64 bit
- Clang: 3.5, ... - 32/64 bit
- Intel: (untested)
- MSVC : 2017(in c++14 mode), ... - 32/64 bit
- Emscripten: 1.38 (clang version 6.0.1)
支持序列化的数据类型
基础数据类型
- 整型:[unsigned] char、[unsigned] short、[unsigned] int、[unsigned] long、[unsigned] long long
- 浮点型:float、double
- 布尔型:bool
C++ STL(25种)
- std::array
- std::bitset
- std::chrono::duration
- std::chrono::time_point
- std::complex
- std::deque
- std::forward_list
- std::list
- std::map
- std::multimap
- std::multiset
- std::optional
- std::pair
- std::set
- std::string
- std::string_view
- std::tuple
- std::unordered_map
- std::unordered_multimap
- std::unordered_multiset
- std::unordered_set
- std::variant
- std::vector
- std::wstring
- boost::array
boost(31种)
- boost::array
- boost::chrono::duration
- boost::chrono::time_point
- boost::optional
- boost::variant
- boost::container::deque
- boost::container::string
- boost::container::wstring
- boost::container::vector
- boost::container::static_vector
- boost::container::stable_vector
- boost::container::list
- boost::container::slist
- boost::container::map
- boost::container::multimap
- boost::container::set
- boost::container::multiset
- boost::container::flat_map
- boost::container::flat_multimap
- boost::container::flat_set
- boost::container::flat_multiset
- boost::unordered_map
- boost::unordered_multimap
- boost::unordered_set
- boost::unordered_multiset
- boost::fusion::pair
- boost::fusion::tuple
- boost::fusion::vector
- boost::fusion::list
- boost::fusion::map
- boost::fusion::set
Qt(6种)
- QByteArray
- QList
- QMap
- QString
- QStringList
- QVector
yas(2种)
- yas::intrusive_buffer
- yas::shared_buffer
Benchmark性能统计
引用:https://github.com/thekvs/cpp-serializers?tab=readme-ov-file
以下统计结果是在运行Ubuntu 16.04的英特尔酷睿i7处理器的典型台式计算机上运行1000000次串行化-去串行化操作50次,然后对结果求平均值而获得的。
使用的库的确切版本是:
- thrift 0.12.0
- protobuf 3.7.0
- boost 1.69.0
- msgpack 3.1.1
- cereal 1.2.2
- avro 1.8.2
- capnproto 0.7.0
- flatbuffers 1.10.0
- YAS 7.0.2
序列化后对象字节总数柱状图如下
序列化平均总时间(ms)柱状图如下
示例代码
asis
asis在这里表示序列化时,数据按照它本来的样子输入、输出,不关心是否使用了压缩。
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct type {
type()
:c{1}
,i(33)
,u(44)
{}
std::uint8_t c;
std::uint32_t i;
std::uint64_t u;
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT("type", c, yas::asis(i), u);
}
};
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.c = 0;
t2.u = 0;
constexpr std::size_t flags = yas::mem|yas::binary|yas::compacted;
auto buf = yas::save<flags>(t1);
assert(buf.size == 7+1+4+1); // 7 - YAS header, 1 - uint8, 4 - nonpacked uint32, 1 - packed uint64
yas::load<flags>(buf, t2);
assert(t1.c == t2.c && t1.i == t2.i);
}
/***************************************************************************/
serialize成员函数,如何序列化这个类的成员。
yas::mem:表示内存流
yas::binary:表示归档类型为二进制
yas::compacted:表示序列化时对数据进行压缩(实际yas只支持整型数据的压缩,后面在单独文章中分析yas压缩的原理)
大家肯定对YAS_OBJECT这个宏做了什么,感到特别好奇,请看下图将其展开后的内容。
make_object会生成一个object对象,make_val会生成一个value对象
在yas的底层,有针对object和value对象专门的结构体模板特化序列化结构体。
那么object和value到底是什么呢?请看下图:
之所以它们的结构体中都有key和klen,主要是在归档为json时需要。
base/derived
下面的代码主要展示存在继承关系的结构体,怎么进行实现序列化函数。
#include <yas/serialize.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct base {
std::uint32_t x;
base()
:x()
{}
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT(nullptr /* reserved */, x);
}
};
struct derived: base {
std::uint32_t y;
derived()
:y()
{}
template<typename Ar>
void serialize(Ar &ar) {
auto &x = yas::base_object<base>(*this);
ar & YAS_OBJECT(nullptr /* reserved */, y, x);
}
};
/***************************************************************************/
int main() {
derived t1, t2;
t1.x = 33;
t1.y = 44;
constexpr std::size_t flags = yas::mem|yas::json;
yas::shared_buffer buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
assert(t2.x == 33 && t2.y == 44);
}
/***************************************************************************/
这段代码的核心就是在derived的serialize的函数中加一行 auto &x = yas::base_object<base>(*this); 并将x添加到序列化列表中。
nested
下面这段代码主要针对类/结构体之间存在has-a关系时,如果进行序列化。
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct data {
data()
:s("33")
{}
std::string s;
// one member-function for save/load
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT("data", s);
}
};
struct type {
type()
:i(33)
,d()
{}
std::uint32_t i;
data d;
// one member-function for save/load
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT("type", i, d);
}
};
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.d.s.clear();
constexpr std::size_t flags = yas::mem|yas::json;
auto buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
assert(t1.i == t2.i && t1.d.s == t2.d.s);
}
/***************************************************************************/
free function
free function的含义,其实是相对member function来说的,也就是serialize函数在结构体外部实现
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct type {
type()
:i(33)
,d(.34)
,s("35")
,v({36, 37, 38})
{}
std::uint32_t i;
double d;
std::string s;
std::vector<std::uint32_t> v;
};
// one free-function serializer/deserializer
template<typename Ar>
void serialize(Ar &ar, type &t) {
ar & YAS_OBJECT_NVP(
"type"
,("i", t.i)
,("d", t.d)
,("s", t.s)
,("v", t.v)
);
}
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.d = 0;
t2.s.clear();
t2.v.clear();
constexpr std::size_t flags = yas::mem|yas::json;
auto buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
// TODO: stackoverflow.com/questions/17333
assert(t1.i == t2.i && t1.d == t2.d && t1.s == t2.s && t1.v == t2.v);
}
/***************************************************************************/
one free-function serializer/deserializer
下面的代码主要描述在一个free function中如何分别实现序列化和反序列化
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct type {
type()
:i(33)
,d(.34)
,s("35")
,v({36, 37, 38})
{}
std::uint32_t i;
double d;
std::string s;
std::vector<int> v;
};
// one free-function serializer/deserializer
template<typename Ar>
void serialize(Ar &ar, type &t) {
if ( Ar::is_writable() ) { // constexpr
ar & YAS_OBJECT_NVP(
"type"
,("i", t.i)
,("d", t.d)
,("s", t.s)
,("v", t.v)
);
} else {
ar & YAS_OBJECT_NVP(
"type"
,("i", t.i)
,("d", t.d)
,("s", t.s)
,("v", t.v)
);
}
}
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.d = 0;
t2.s.clear();
t2.v.clear();
constexpr std::size_t flags = yas::mem|yas::json;
auto buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
// TODO: stackoverflow.com/questions/17333
assert(t1.i == t2.i && t1.d == t2.d && t1.s == t2.s && t1.v == t2.v);
}
/***************************************************************************/
one member-function for save/load
在成员函数中实现序列化和反序列化。
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct type {
type()
:i(33)
,d(.33)
,s("33")
,v({33, 33, 33})
{}
std::uint32_t i;
double d;
std::string s;
std::vector<int> v;
// one member-function for save/load
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT_NVP(
"type"
,("i", i)
,("d", d)
,("s", s)
,("v", v)
);
}
};
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.d = 0;
t2.s.clear();
t2.v.clear();
constexpr std::size_t flags = yas::mem|yas::json;
auto buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
// TODO: stackoverflow.com/questions/17333
assert(t1.i == t2.i && t1.d == t2.d && t1.s == t2.s && t1.v == t2.v);
}
/***************************************************************************/
大家不要被YAS_OBJECT_NVP吓到,它是另外一个类似YAS_OBJECT的宏,区别是最后多了NVP,NVP可以理解为name value pair,使用这个宏,需用手动写key的字符串,因此手动写,使得用户可以根据自己的喜好,定义一个与变量名不同的key。值得注意的是,如果归档为json,序列化和反序列化要使用相同的key。
split member-functions for save/load
使用分离的serialize成员函数实现数据的序列化和反序列化。值得注意的是,如果归档为json,序列化和反序列化要使用相同的key。
#include <yas/serialize.hpp>
#include <yas/std_types.hpp>
#undef NDEBUG
#include <cassert>
/***************************************************************************/
struct type {
type()
:i(33)
,d(.34)
,s("35")
,v({36, 37, 38})
{}
std::uint32_t i;
double d;
std::string s;
std::vector<int> v;
// split member-functions for save/load
// save
template<typename Ar>
void serialize(Ar &ar) const {
ar & YAS_OBJECT_NVP(
"type"
,("i", i)
,("d", d)
,("s", s)
,("v", v)
);
}
// load
template<typename Ar>
void serialize(Ar &ar) {
ar & YAS_OBJECT_NVP(
"type"
,("i", i)
,("d", d)
,("s", s)
,("v", v)
);
}
};
/***************************************************************************/
int main() {
type t1, t2;
t2.i = 0;
t2.d = 0;
t2.s.clear();
t2.v.clear();
constexpr std::size_t flags = yas::mem|yas::json;
auto buf = yas::save<flags>(t1);
yas::load<flags>(buf, t2);
// TODO: stackoverflow.com/questions/17333
assert(t1.i == t2.i && t1.d == t2.d && t1.s == t2.s && t1.v == t2.v);
}
/***************************************************************************/
总结
通过上面的分享,相信你对yas是什么及怎么使用,已经有了大致的了解,如果想了解更多关于源码的分析,可以我注释后的源码(yas_comment: 对yas源码进行注释)。
也可以关注我,等待我后续关于源码的解析文章。