yas源码解析 之 yas是什么

简介

github源码地址:

GitHub - niXman/yas: Yet Another SerializationYet Another Serialization. Contribute to niXman/yas development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/niXman/yas

个人注释gitee地址:yas_comment: 对yas源码进行注释icon-default.png?t=N7T8https://gitee.com/AriesSun/yas_comment

yas是Yet Another Serialization的简称,又一个序列化的库。yas是基于C++语言开发的header only的序列化的库,支持的归档类型包括二进制、json、文本文件等。

与之类似的序列化库包括:thrift、protobuf、boost、msgpack、cereal、avro、capnproto、flatbuffers等。

特点

  1. 为替代boost.serialization而创建,因为boost.serialization的序列化速度不够快
  2. header only library
  3. does not depend on third-party libraries or boost
  4. 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源码进行注释)。

也可以关注我,等待我后续关于源码的解析文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值