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)
serialize
和deserialize
成員函數(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生成的類型逐抑,ProtoDynSerializer
和ProtoSerializer
的處理是一樣的,區(qū)別在于用戶定義類型的用法屹蚊。
注意:
- 序列化和反序列化調(diào)用形式基本是一致的厕氨,只是
serialize
和deserialize
函數(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)