yymodel详解

发布时间:2023-05-18

一、yymodel源码

yymodel是一款C++编写的高性能序列化和反序列化库,常用于网络通信、分布式系统等领域。该库源码开放,可在Github上进行下载和使用。 以下是yymodel库中的一段示例代码:

#include <yymodel/yymodel.h>
using namespace yymodel;
struct Person {
    std::string name;
    int age;
    bool married;
};
YYMODEL_BEGIN(Person)
    YYMODEL_PROPERTY(name)
    YYMODEL_PROPERTY(age)
    YYMODEL_PROPERTY(married)
YYMODEL_END()
int main() {
    Person p1;
    p1.name = "Alice";
    p1.age = 22;
    p1.married = false;
    // 序列化
    std::string buf;
    yymodel::to_json(p1, buf);
    std::cout << buf << std::endl;
    // 反序列化
    Person p2;
    yymodel::from_json(buf, p2);
    return 0;
}

该示例代码实现了对Person结构体的序列化和反序列化,其中YYMODEL_BEGINYYMODEL_END宏用于定义该结构体需要序列化和反序列化的属性。to_jsonfrom_json函数分别是将该结构体转化为json格式和将json格式转换为该结构体的函数。

二、yymodel面试题

在使用yymodel过程中,可能会遇到以下面试题:

1. 为什么使用yymodel而不是其他序列化库?

答:yymodel具有以下几个特点:支持多种序列化格式(json、msgpack、protobuf)、高效(序列化和反序列化速度非常快)、易于使用(定义一个结构体即可)。因此,在需要高性能、易于使用、支持多种序列化格式的场景下,yymodel是一个不错的选择。

2. 如何定义需要序列化和反序列化的属性?

答:使用YYMODEL_BEGINYYMODEL_END宏定义需要序列化和反序列化的属性,如:

struct Person {
    std::string name;
    int age;
    bool married;
};
YYMODEL_BEGIN(Person)
    YYMODEL_PROPERTY(name)
    YYMODEL_PROPERTY(age)
    YYMODEL_PROPERTY(married)
YYMODEL_END()

3. yymodel支持哪些序列化格式?

答:yymodel支持json、msgpack、protobuf三种序列化格式。

三、yymodel原理

yymodel的原理是通过模板和宏定义将结构体转换为序列化和反序列化的函数。具体地,使用YYMODEL_BEGINYYMODEL_END宏定义需要序列化和反序列化的属性,每个属性通过YYMODEL_PROPERTY宏进行定义。然后,使用模板类将结构体转换为序列化和反序列化的函数。 以下是yymodel源码中的to_json函数模板:

template <typename T>
void to_json(const T& obj, std::string& out, JsonStyle style = JsonStyle::kReadableCompact) {
    using AdapterType = Adapter<T, typename object_adapter<T>::value_type>;
    TemplateWriter<std::string> writer(out);
    AdapterType adapter(obj);
    adapter.write(writer, style);
}

该函数使用模板,其中T表示序列化的结构体类型,Adapter表示结构体的适配器。to_json函数将一个结构体对象转换为json格式的字符串。

四、yymodel解析多层嵌套

yymodel支持多层嵌套的序列化和反序列化,示例如下:

struct Address {
    std::string country;
    std::string province;
    std::string city;
};
YYMODEL_BEGIN(Address)
    YYMODEL_PROPERTY(country)
    YYMODEL_PROPERTY(province)
    YYMODEL_PROPERTY(city)
YYMODEL_END()
struct Person {
    std::string name;
    int age;
    Address addr;
};
YYMODEL_BEGIN(Person)
    YYMODEL_PROPERTY(name)
    YYMODEL_PROPERTY(age)
    YYMODEL_PROPERTY(addr)
YYMODEL_END()

该代码定义了两个结构体,Person结构体包含Address结构体,代码中的YYMODEL_PROPERTY(addr)表示addr是一个Address类型的属性,需要进行序列化和反序列化。

五、yymodel实现原理

yymodel实现原理是通过模板类和适配器实现的。如下代码定义了一个适配器类:

template <typename T, typename TA>
class Adapter {
public:
    typedef TA Type;
    typedef TypeAdapterTraits<TA> Traits;
    explicit Adapter(const T& obj) : obj_(obj) {}
    template <typename Writer>
    void write(Writer& writer, JsonStyle style) const {
        write_internal<Writer, Traits::is_object::value>(writer, obj_, style);
    }
private:
    template <typename Writer, bool kIsObject, typename TV>
    struct WriteProperty;
    template <typename Writer, typename TV>
    struct WriteProperty<Writer, true, TV> {
        static void write(Writer& writer, const std::string& key, const TV& value) {
            writer.Key(key);
            Adapter<TV, typename Traits::template ArgType<TV>::type> adapter(value);
            adapter.write(writer, JsonStyle::kCompact);
        }
    };
    template <typename Writer, typename TV>
    struct WriteProperty<Writer, false, TV> {
        static void write(Writer& writer, const std::string& key, const TV& value) {
            Adapter<TV, typename Traits::template ArgType<TV>::type> adapter(value);
            adapter.write(writer, JsonStyle::kCompact);
        }
    };
    template <typename Writer, typename T1>
    void write_internal(Writer& writer, const T1& v, JsonStyle style) const {
        switch (TA::kType) {
            case TypeID::kBool:
                writer.Bool(v);
                break;
            case TypeID::kInt:
                writer.Int(v);
                break;
            case TypeID::kUInt:
                writer.Uint(v);
                break;
            case TypeID::kDouble:
                writer.Double(v);
                break;
            case TypeID::kString:
                writer.String(v);
                break;
            case TypeID::kArray:
                writer.StartArray();
                for (size_t i = 0; i < v.size(); i++) {
                    WriteProperty<Writer, Traits::is_object::value, typename T1::value_type>::write(writer, "", v[i]);
                }
                writer.EndArray();
                break;
            case TypeID::kObject: {
                writer.StartObject();
                TA::Foreach(v, [&](const typename Traits::KeyType& key, const typename Traits::ValueType& value) {
                    WriteProperty<Writer, true, typename Traits::ValueType>::write(writer, key.to_string(), value);
                });
                writer.EndObject();
                break;
            }
            default:
                assert(false);
                break;
        }
    }
    const T& obj_;
};

该适配器类将结构体转换为json字符串的过程分解为了多个子过程,包括写入属性名,写入属性值等。

六、yymodel使用

yymodel的使用非常简单,只需要定义一个需要序列化和反序列化的结构体,然后使用to_jsonfrom_json函数将该结构体转换为json格式即可。 以下是一个示例代码:

struct Person {
    std::string name;
    int age;
    bool married;
};
YYMODEL_BEGIN(Person)
    YYMODEL_PROPERTY(name)
    YYMODEL_PROPERTY(age)
    YYMODEL_PROPERTY(married)
YYMODEL_END()
int main() {
    Person p1;
    p1.name = "Alice";
    p1.age = 22;
    p1.married = false;
    // 序列化
    std::string buf;
    yymodel::to_json(p1, buf);
    std::cout << buf << std::endl;
    // 反序列化
    Person p2;
    yymodel::from_json(buf, p2);
    return 0;
}

该示例代码将Person结构体转换为json字符串,并从json字符串中读取该结构体。

七、yymodel protobuf

yymodel支持protobuf序列化和反序列化,这里给出protobuf的示例代码:

struct Address {
    std::string country;
    std::string province;
    std::string city;
};
YYMODEL_BEGIN(Address)
    YYMODEL_PROPERTY(country)
    YYMODEL_PROPERTY(province)
    YYMODEL_PROPERTY(city)
YYMODEL_END()
struct Person {
    std::string name;
    int age;
    Address addr;
};
YYMODEL_BEGIN(Person)
    YYMODEL_PROPERTY(name)
    YYMODEL_PROPERTY(age)
    YYMODEL_PROPERTY(addr)
YYMODEL_END()
// 定义protobuf格式
enum {
    kPersonProtoFieldNumber = 1,
    kPersonNameProtoFieldNumber = 1,
    kPersonAgeProtoFieldNumber = 2,
    kPersonAddrProtoFieldNumber = 3,
    kPersonAddrCountryProtoFieldNumber = 1,
    kPersonAddrProvinceProtoFieldNumber = 2,
    kPersonAddrCityProtoFieldNumber = 3,
};
// Person结构体转换为protobuf格式
google::protobuf::Message* person_to_proto(const Person& person) {
    PersonProto* proto = new PersonProto();
    proto->set_name(person.name);
    proto->set_age(person.age);
    AddressProto* addr_proto = proto->mutable_addr();
    addr_proto->set_country(person.addr.country);
    addr_proto->set_province(person.addr.province);
    addr_proto->set_city(person.addr.city);
    return proto;
}
// protobuf格式转换为Person结构体
void proto_to_person(google::protobuf::Message* proto, Person& person) {
    PersonProto* person_proto = dynamic_cast<PersonProto*>(proto);
    person.name = person_proto->name();
    person.age = person_proto->age();
    const AddressProto& addr_proto = person_proto->addr();
    person.addr.country = addr_proto.country();
    person.addr.province = addr_proto.province();
    person.addr.city = addr_proto.city();
}
int main() {
    Person person;
    person.name = "Alice";
    person.age = 22;
    person.addr.country = "China";
    person.addr.province = "Beijing";
    person.addr.city = "Haidian";
    // person结构体转为protobuf格式
    google::protobuf::Message* proto = person_to_proto(person);
    // 序列化
    std::string buf;
    yymodel::to_protobuf(proto, buf);
    // 反序列化
    Person person2;
    google::protobuf::Message* proto2 = nullptr;
    if (yymodel::from_protobuf(buf, proto2)) {
        proto_to_person(proto2, person2);
    }
    return 0;
}

该示例代码将Person结构体使用protobuf序列化,在序列化和反序列化时使用yymodel中的to_protobuffrom_protobuf函数进行处理。