閑來無事發現了一個基於C++實現的序列化工具,相比於其他(比如Boost serialization或Google protobuf,恰巧都用過,以后再介紹),使用簡單,感覺不錯,下面做個摸索。
cereal介紹
cereal是一個開源的(BSD License)、輕量級的、支持C++11特性的、僅僅包含頭文件實現的、跨平台的C++序列化庫。它可以將任意的數據類型序列化成不同的表現形式,比如二進制、XML格式或JSON。cereal的設計目標是快速、輕量級、易擴展——它沒有外部的依賴關系,而且可以很容易的和其他代碼封裝在一塊或者單獨使用。
cereal支持標准庫的幾乎每一個類型的序列化。cereal也完全支持繼承和多態。由於cereal被設計為一個精簡、快速的庫,它不像其他序列化庫(比如Boost)在同一層次上會進行對象跟蹤,這也導致了它不支持原始指針(raw pointer)和引用,但是智能指針(比如std::shared_ptr和std::unique_ptr)是沒有問題的。
cereal適用於基於C++11標准的各種編譯器
cereal使用了一些C++11的新特性,因此需要一個兼容性更好的的C++編譯器才能正常工作。已被驗證可用的編譯器有g++4.7.3、clang++3.3、MSVC2013,或者更新版本。
它也可能可以在老版本編譯器上工作,但並不保證完全支持。當使用g++或clang++編譯器時,cereal同時需要libstdc++和libc++庫。
cereal:更快速,更好的壓縮
在簡單的性能測試中,cereal通常比Boost的序列化庫速度更快,而且產生的二進制形式占用更少的空間,尤其是針對更小的對象。cereal使用了C++中的速度最快的XML和JSON解析器和包裝器。
cereal是可擴展的
cereal提供了對標准庫的序列化支持,比如二進制的,XML和JSON序列化器。
cereal的源代碼相比Boost來講,更容易理解和擴展。 如果你需要別的東西,cereal可以很容易地擴展,比如添加自定義序列化存檔或類型。
cereal是易於使用的
在代碼增加cereal序列化功能可以簡化為包含一個頭文件,寫一個序列化函數。無論是從概念上還是代碼層次上,cereal的功能都是自文檔化的。
如果你使用錯誤,cereal盡可能的在編譯期觸發靜態斷言。
對於Boost使用者來說,cereal提供了相似的語法,如果你使用過Boost的序列化庫,你會發現cereal的語法看起來很熟悉。
如果你是從Boost轉向使用cereal,一定要閱讀這個過渡指南:http://uscilab.github.io/cereal/transition_from_boost.html
簡單的使用
好吧,廢話就這么多,先上一個簡單的事例:
std::ofstream os("my.xml");
cereal::XMLOutputArchive archive(os);
int age = 26;
std::string name = "lizheng";
archive(CEREAL_NVP(age), cereal::make_nvp("Name", name));
以上代碼完成了對一個int類型和string類型的xml序列化實現。結果如下:
<?xml version="1.0" encoding="utf-8"?>
<cereal>
<age>26</age>
<Name>lizheng</Name>
</cereal>
注意上面代碼中的cereal::XMLOutputArchive,其實還有針對JSON、二進制序列化的類,如果是序列化為JSON串,結果如下(代碼在最下面):
{
"age": 26,
"Name": "lizheng"
}
我的Demo
完整代碼如下(或點此下載完整工程,或者從我的github下載包括cereal頭文件在內的整個項目):
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 #include "cereal/archives/binary.hpp"
5 #include "cereal/archives/xml.hpp"
6 #include "cereal/archives/json.hpp"
7 #include "cereal/types/unordered_map.hpp"
8 #include "cereal/types/memory.hpp"
9 #include "cereal/types/string.hpp" //一定要包含此文件,否則無法將std::string序列化為二進制形式,請看:https://github.com/USCiLab/cereal/issues/58
10
11 using namespace std;
12
13 struct MyRecord
14 {
15 int x, y;
16 float z;
17
18 template <class Archive>
19 void serialize(Archive & ar)
20 {
21 ar(x, y, z);
22 }
23
24 friend std::ostream& operator<<(std::ostream& os, const MyRecord& mr);
25 };
26
27 std::ostream& operator<<(std::ostream& os, const MyRecord& mr)
28 {
29 os << "MyRecord(" << mr.x << ", " << mr.y << "," << mr.z << ")\n";
30 return os;
31 }
32
33 struct SomeData
34 {
35 int32_t id;
36 std::shared_ptr<std::unordered_map<uint32_t, MyRecord>> data;
37
38 SomeData(int32_t id_=0) : id(id_), data(new std::unordered_map<uint32_t, MyRecord>)
39 {
40
41 }
42
43 template <class Archive>
44 void save(Archive & ar) const
45 {
46 ar(id, data);
47 }
48
49 template <class Archive>
50 void load(Archive & ar)
51 {
52 ar(id, data);
53 }
54
55 void push(uint32_t, const MyRecord& mr)
56 {
57 data->insert(std::make_pair(100, mr));
58 }
59
60 void print()
61 {
62 std::cout << "ID : " << id << "\n";
63 if (data->empty())
64 return;
65 for (auto& item : *data)
66 {
67 std::cout << item.first << "\t" << item.second << "\n";
68 }
69 }
70 };
71
72 void Serialization_XML()
73 {
74 {
75 std::ofstream os("my.xml");
76
77 cereal::XMLOutputArchive archive(os);
78
79 int age = 26;
80 std::string name = "lizheng";
81
82 //#define CEREAL_NVP(T) ::cereal::make_nvp(#T, T)
83 archive(CEREAL_NVP(age), cereal::make_nvp("Name", name));
84
85 //os.close(); //注意:這里不能顯示關閉ofstream,否則序列化無法寫入到文件
86 }
87
88 {
89 std::ifstream is("my.xml");
90 cereal::XMLInputArchive archive(is);
91
92 int age;
93 std::string name;
94
95 archive(age, name);
96 std::cout << "Age: " << age << "\n" << "Name: " << name << "\n";
97 }
98 }
99
100 void Serialization_JSON()
101 {
102 {
103 std::ofstream os("my.json");
104 cereal::JSONOutputArchive archive(os);
105
106 int age = 26;
107 std::string name = "lizheng";
108
109 archive(CEREAL_NVP(age), cereal::make_nvp("Name", name));
110 }
111
112 {
113 std::ifstream is("my.json");
114 cereal::JSONInputArchive archive(is);
115
116 int age;
117 std::string name;
118
119 archive(age, name);
120 std::cout << "Age: " << age << "\n" << "Name: " << name << "\n";
121 }
122 }
123
124
125 void Serialization_Binary()
126 {
127 {
128 std::ofstream os("my.binary", std::ios::binary);
129 cereal::BinaryOutputArchive archive(os);
130
131 int age = 26;
132 std::string name = "lizheng";
133
134 archive(CEREAL_NVP(age), CEREAL_NVP(name));
135 }
136 {
137 std::ifstream is("my.binary", std::ios::binary);
138 cereal::BinaryInputArchive archive(is);
139
140 int age;
141 std::string name;
142
143 archive(age, name);
144 std::cout << "Age: " << age << "\n" << "Name: " << name << "\n";
145 }
146 }
147
148 void Serialization_Obj()
149 {
150 {
151 std::ofstream os("obj.cereal", std::ios::binary);
152 cereal::BinaryOutputArchive archive(os);
153
154 MyRecord mr = { 1, 2, 3.0 };
155
156 SomeData myData(1111);
157 myData.push(100, mr);
158
159 archive(myData);
160 }
161 {
162 std::ifstream is("obj.cereal", std::ios::binary);
163 cereal::BinaryInputArchive archive(is);
164
165 SomeData myData;
166 archive(myData);
167 myData.print();
168 }
169 }
170
171
172 int main()
173 {
174 Serialization_XML(); std::cout << "----------------------\n";
175
176 Serialization_JSON(); std::cout << "----------------------\n";
177
178 Serialization_Binary(); std::cout << "----------------------\n";
179
180 Serialization_Obj(); std::cout << "----------------------\n";
181
182 getchar();
183 return 0;
184 }

