TinySerializer框架的用法

0. 簡(jiǎn)介

實(shí)現(xiàn)了一個(gè)可擴(kuò)展的微型C++序列化框架tiny-serializer词裤。

項(xiàng)目地址:https://github.com/david-pp/tiny-serializer

依賴:

  • C++11
  • protobuf
  • boost/any.hpp

1. 用法(靜態(tài)的ProtoSerializer

注意:本節(jié)演示代碼位于該項(xiàng)目的:exmaple/demo_serialize.cpp叔锐。

1.1 序列化基本類型

支持下面的類型:

  • 整數(shù):uint8_t/int8_t, uint16_t/int16_t, int32_t/uint32_t, int64_t/uint64_t, bool
  • 浮點(diǎn)數(shù):float, double
  • 字符串:std::string。(注意:不支持C風(fēng)格的字符串)

注意:同種類型的可以隨意調(diào)整马篮,不會(huì)影響序列化。如:uint32_t序列化的數(shù)據(jù)怜奖,使用uint8_t/uint64_t也可以進(jìn)行正常的反序列化浑测。

例子:

  • 整數(shù)

            uint32_t v1 = 1024;
            std::string data = serialize(v1);
    
            uint64_t v2 = 0;
            deserialize(v2, data);
    
            std::cout << v2 << std::endl;
    
  • 字符串

            std::string v1 = "Hello David++!";
            std::string data = serialize(v1);
    
            std::string v2;
            if (deserialize(v2, data))
                std::cout << v2 << std::endl;
            else
                std::cout << "error happens !" << std::endl;
    

1.2 序列化Protobuf生成的結(jié)構(gòu)

        PlayerProto p1;
        p1.set_id(1024);
        p1.set_name("david");
        p1.add_quests(1);
        p1.add_quests(2);
        p1.mutable_weapon()->set_type(2);
        p1.mutable_weapon()->set_name("Sword");


        std::string data = serialize(p1);

        PlayerProto p2;
        if (deserialize(p2, data))
            std::cout << p2.ShortDebugString() << std::endl;
        else
            std::cout << "error happens !" << std::endl;

1.3 序列化STL容器

支持的容器如下:

  • Sequence: vector, list, deque
  • Set: set, multiset烦周。
  • Map: map, multimap尽爆。
  • HashSet: unordered_set, unordered_multiset
  • HashMap: unordered_map, unordered_multimap读慎。

注意:

  • 同種類型的容器也是可以互換漱贱,而不影響序列化。
  • 支持容器的任意組合和嵌套夭委。

例子:

  • 簡(jiǎn)單容器:vector<uint8_t>序列化的數(shù)據(jù)幅狮,使用list<uint32_t>進(jìn)行反序列化

         std::vector<uint8_t> v1 = {1, 2, 3, 4, 5, 6};
         std::string data = serialize(v1);
    
         std::list<uint32_t> v2;
         deserialize(v2, data);
    
         for (auto &v : v2)
             std::cout << v << ",";
         std::cout << std::endl;
    
  • 容器嵌套:map嵌套vector示例

        std::map<uint32_t, std::vector<PlayerProto>> v1 = {
                {1024, {p, p, p}},
                {1025, {p, p}},
                {1026, {p}},
        };
    
        std::string data = serialize(v1);
    
        std::map<uint32_t, std::vector<PlayerProto>> v2;
        deserialize(v2, data);
    
        for (auto &v : v2) {
            std::cout << v.first << std::endl;
            for (auto& player : v.second)
                std::cout << "\t - " << player.ShortDebugString() << std::endl;
        }
    

1.4 序列化用戶自定義類型

對(duì)用戶自定義類型序列化的支持,分兩種方式:

  • 侵入式:實(shí)現(xiàn)serializedeserialize成員函數(shù)株灸。
  • 非侵入式:對(duì)該用戶類型的Serializer進(jìn)行偏特化崇摄。

例子

  • 侵入式演示:

    struct Weapon {
        uint32_t type = 0;
        std::string name = "";
    
        //
        // intrusive way
        //
        std::string serialize() const {
            WeaponProto proto;
            proto.set_type(type);
            proto.set_name(name);
            return proto.SerializeAsString();
        }
    
        bool deserialize(const std::string &data) {
            WeaponProto proto;
            if (proto.ParseFromString(data)) {
                type = proto.type();
                name = proto.name();
                return true;
            }
            return false;
        }
    };
    
    // serialize & deserialize
    
    Weapon w;
    w.type = 22;
    w.name = "Blade";
    
    std::string data = serialize(w);
    
    Weapon w2;
    deserialize(w2, data);
    std::cout << w2.type << " - " << w2.name << std::endl;
    
    
  • 非侵入式演示:

    struct Player {
        uint32_t id = 0;
        std::string name = "";
        std::map<uint32_t, std::vector<Weapon>> weapons_map;
    };
    
    // non-intrusive way
    template<>
    struct ProtoSerializer<Player> {
        std::string serialize(const Player &p) const {
            PlayerProto proto;
            proto.set_id(p.id);
            proto.set_name(p.name);
            // complex object
            proto.set_weapons_map(::serialize(p.weapons_map));
            return proto.SerializeAsString();
        }
    
        bool deserialize(Player &p, const std::string &data) const {
            PlayerProto proto;
            if (proto.ParseFromString(data)) {
                p.id = proto.id();
                p.name = proto.name();
                // complex object
                ::deserialize(p.weapons_map, proto.weapons_map());
                return true;
            }
            return false;
        }
    };
    
    // serialize & deserialize
    Player p;
    p.init();
    
    std::string data = serialize(p);
    
    Player p2;
    deserialize(p2, data);
    p2.dump();
    

2. 反射式用法(動(dòng)態(tài)的ProtoDynSerializer

對(duì)于基本類型、STL慌烧、Protobuf生成的類型逐抑,ProtoDynSerializerProtoSerializer的處理是一樣的,區(qū)別在于用戶定義類型的用法屹蚊。

注意:

  • 序列化和反序列化調(diào)用形式基本是一致的厕氨,只是serializedeserialize函數(shù)多了一個(gè)模板參數(shù)ProtoDynSerializer进每。
  • 本節(jié)演示代碼位于:exmaple/demo_serializer_dyn.cpp

2.1 基本類型命斧、STL田晚、Protobuf生成類型的序列化

  • 以Probuf生成類型為例,其他的類似:
        PlayerProto p1;
        p1.set_id(1024);
        p1.set_name("david");
        p1.add_quests(1);
        p1.add_quests(2);
        p1.mutable_weapon()->set_type(2);
        p1.mutable_weapon()->set_name("Sword");
    
    
        std::string data = serialize<ProtoDynSerializer>(p1);
    
        PlayerProto p2;
        if (deserialize<ProtoDynSerializer>(p2, data))
            std::cout << p2.ShortDebugString() << std::endl;
        else
            std::cout << "error happens !" << std::endl;
    ```


### 2.2 用戶自定義類型的序列化

- 用戶自定義類型:

    ```c++
    struct Weapon {
        uint32_t type = 0;
        std::string name = "";
    };
    
    struct Player {
        uint32_t id = 0;
        std::string name = "";
        std::vector<uint32_t> quests;
        Weapon weapon;
        std::map<uint32_t, Weapon> weapons;
        std::map<uint32_t, std::vector<Weapon>> weapons_map;
    };
    ```
- 用戶自定義類型到`Proto`的映射

    ```c++
    // Mapping
    RUN_ONCE(Mapping) {
    
        ProtoMappingFactory::instance().declare<Weapon>("Weapon")
                .property<ProtoDynSerializer>("type", &Weapon::type, 1)
                .property<ProtoDynSerializer>("name", &Weapon::name, 2);
    
        ProtoMappingFactory::instance().declare<Player>("Player", "PlayerDynProto")
                .property<ProtoDynSerializer>("id", &Player::id, 1)
                .property<ProtoDynSerializer>("name", &Player::name, 2)
                .property<ProtoDynSerializer>("quests", &Player::quests, 3)
                .property<ProtoDynSerializer>("weapon", &Player::weapon, 4)
                .property<ProtoDynSerializer>("weapon2", &Player::weapons, 5)
                .property<ProtoDynSerializer>("weapons_map", &Player::weapons_map, 6);
    
        // MUST!!! CREATE DESCRIPTORS
        ProtoMappingFactory::instance().createAllProtoDescriptor();
    }
    ```
    
    執(zhí)行`ProtoMappingFactory::instance().createAllProtoDefine()`国葬,可以得到它們映射的Proto定義:
    
    ```proto
    // Weapon -> WeaponDynProto
    message WeaponDynProto {
      optional bytes type = 1;
      optional bytes name = 2;
    }
    
    // Player -> PlayerDynProto
    message PlayerDynProto {
      optional bytes id = 1;
      optional bytes name = 2;
      optional bytes quests = 3;
      optional bytes weapon = 4;
      optional bytes weapon2 = 5;
      optional bytes weapons_map = 6;
    }
    ```

- 序列化/反序列化

    ```c++
    try {
        Player p;
        p.init();
    
        std::string data = serialize<ProtoDynSerializer>(p);
    
        Player p2;
        deserialize<ProtoDynSerializer>(p2, data);
        p2.dump();
    } catch (const std::exception& e) {
        std::cout << "Error Happens: " << e.what() << std::endl;
    }
    ```

## 3.靜態(tài) vs. 動(dòng)態(tài)

- 推薦使用靜態(tài)玩法:
     - 靜態(tài)執(zhí)行效率高于動(dòng)態(tài)贤徒。
     - 靜態(tài)出錯(cuò)會(huì)在編譯器暴露,而動(dòng)態(tài)出錯(cuò)時(shí)會(huì)在運(yùn)行時(shí)拋出異常汇四。
     
- 動(dòng)態(tài)的好處:用戶自定義類型使用起來更加方便接奈,不要額外定義Proto和實(shí)現(xiàn)序列化的約定。


- 性能簡(jiǎn)單對(duì)比:動(dòng)態(tài)/靜態(tài) ~= 1.3倍

    ```bash
    $ time `./demo_serialize_dyn 10000 > /dev/null`
    
    real    0m7.522s
    user    0m7.051s
    sys     0m0.435s
    
    $ time `./demo_serialize 10000 > /dev/null`
    
    real    0m5.858s
    user    0m5.460s
    sys     0m0.373s
    ```



### 4. 進(jìn)一步了解

 - [TinySerializer的設(shè)計(jì)](https://github.com/david-pp/tiny-serializer/blob/master/tiny-serializer-design.md)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末船殉,一起剝皮案震驚了整個(gè)濱河市鲫趁,隨后出現(xiàn)的幾起案子斯嚎,更是在濱河造成了極大的恐慌利虫,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堡僻,死亡現(xiàn)場(chǎng)離奇詭異糠惫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钉疫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門硼讽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人牲阁,你說我怎么就攤上這事固阁。” “怎么了城菊?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵备燃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我凌唬,道長(zhǎng)并齐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任客税,我火速辦了婚禮况褪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘更耻。我一直安慰自己测垛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布秧均。 她就那樣靜靜地躺著食侮,像睡著了一般脊奋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疙描,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天诚隙,我揣著相機(jī)與錄音,去河邊找鬼起胰。 笑死久又,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的效五。 我是一名探鬼主播地消,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼畏妖!你這毒婦竟也來了脉执?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤戒劫,失蹤者是張志新(化名)和其女友劉穎半夷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迅细,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巫橄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茵典。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湘换。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖统阿,靈堂內(nèi)的尸體忽然破棺而出彩倚,到底是詐尸還是另有隱情,我是刑警寧澤扶平,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布帆离,位于F島的核電站,受9級(jí)特大地震影響蜻直,放射性物質(zhì)發(fā)生泄漏盯质。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一概而、第九天 我趴在偏房一處隱蔽的房頂上張望呼巷。 院中可真熱鬧,春花似錦赎瑰、人聲如沸王悍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)压储。三九已至鲜漩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間集惋,已是汗流浹背孕似。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刮刑,地道東北人喉祭。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像雷绢,于是被迫代替她去往敵國(guó)和親泛烙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容