【原创】 Boost序列化自己手写实现简易版
设计思路
在与多个系统进行网络交互时,序列化是不可缺少的技术。编写一个C++语言的序列化实现,是练习运用模板元编程的绝佳案例,理解C++模板是如何"面向编译期编程"的(业内好像没有这个说法)。序列化对象处理基础数据类型和类类型,boost的序列化功能划分得更细致,基本支持了C++语言的序列化,但是在业务开发中,支持这两种已经足够用了。对于基础数据类型的序列化,需要合理组织序列化的协议格式;对于类类型的序列化,类是由基础数据类型组成的,最终转换为基础数据类型的序列化。
代码思路
序列化实现类class CTextSerialize,反序列化实现类class CTextDeserialize;这两个类都通过业务类的模板函数serialize以传参的方式分别实现序列化和反序列化。class CTextSerialize中的重载函数serialize分别实现基础数据类型和类类型的序列化;class CTextDeserialize中的重载函数deserialize分别实现基础数据类型和类类型的反序列化。这两个类在处理类类型序列化/反序列化时,都调用了class CAccess的静态函数serialize。对于容器vector和map这种特殊的类类型,需要单独实现偏特化的class CAccess。整体代码设计思路需要结合完整代码实现细节进行理解。
完整代码
代码基于C++98进行编写,采用gcc4.8.5编译器编译,测试运行在CentOS7.3环境。
在main函数中,代码分为四段:
第一段,采用输入输出流操作不同基础数据类型的变量;
第二段,使用模板判断变量类型是基础数据类型还是类类型;
第三段,对基础数据类型进行序列化和反序列化;
第四段,对类类型进行序列化和反序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | #include <iostream> #include <sstream> #include <map> #include <vector> #include <stdint.h> using namespace std;
template<typename T> struct is_class_imp{ //采用boost的type_traits的方式判断,判断一个类型是否是一个类类型 typedef char class_type; //一个字节 typedef int32_t non_class_type; //四个字节 template<typename C> static class_type is_class_check(void(C::*)(void)); //类类型匹配到的模板函数 template<typename C> static non_class_type is_class_check(...); //基础类型匹配到的模板函数
static const bool value = (sizeof(is_class_check<T>(0)) == sizeof(class_type)); //value的值在编译期决定 }; template<> struct is_class_imp<string>{ //模板特化,string可以作为基础类型处理,其实是类类型 static const bool value = false; }; template<typename T> struct is_class : is_class_imp<T>{}; //继承
template<bool C_> struct bool_plt{}; //用于编译期条件判断的模板,bool_plt<true>和bool_plt<false>
template<typename C_, typename F1, typename F2> //C_编译期的条件,依据条件判断,动态定义类型F1或F2 struct eval_if{}; template<typename F1, typename F2> //模板偏特化,typename C_ struct eval_if<bool_plt<true>, F1, F2>{ //当C_编译期条件为bool_plt<true>时,定义类型F1 typedef F1 type; }; template<typename F1, typename F2> //模板偏特化,typename C_ struct eval_if<bool_plt<false>, F1, F2>{ //当C_编译期条件为bool_plt<false>时,定义类型F2 typedef F2 type; };
template<typename Archive, typename T> class CAccess //对类类型对象,应该序列化还是反序列化的控制函数 { public: static void serialize(Archive& ar, T& t){ //调用类类型对象的serialize函数,序列化还是反序列化由ar参数决定 t.serialize(ar); } }; template<typename Archive, typename T> struct CFreeMarshall{ //序列化结构体类型 static void invoke(Archive& ar, const T& t){ CAccess<Archive, T>::marshall(ar, t); } }; template<typename Archive, typename T> struct CFreeDemarshall{ //反序列化结构体类型 static void invoke(Archive& ar, T& t){ CAccess<Archive, T>::demarshall(ar, t); } }; template<typename Archive, typename T> struct CFreeInvoke{ //序列化和反序列化统一调用模版函数,在编译期决定调用其一 static void invoke(Archive& ar, T& t){ typedef typename eval_if<typename Archive::is_marshall, //假如ar对象是序列化对象 CFreeMarshall<Archive, T>, //定义序列化类型 CFreeDemarshall<Archive, T> >::type typex; //否则定义反序列化类型 typex::invoke(ar, t); //调用序列化或反序列化函数,在编译期动态判断决定 } };
template<typename Archive, typename T> class CAccess<Archive, vector<T> > //模板偏特化,实现vector容器的序列化和反序列化 { public: static void serialize(Archive& ar, vector<T>& t) //调用序列化或反序列化函数,在编译期动态判断决定 { CFreeInvoke<Archive, vector<T> >::invoke(ar, t); } static void marshall(Archive& ar, const vector<T>& t) //序列化 { int len = t.size(); ar << len << " "; for (int i = 0; i < len; i++) { ar << t[i] << " "; } } static void demarshall(Archive& ar, vector<T>& t) //反序列化 { int len = 0; ar >> len; t.clear(); for (int i = 0; i < len; i++) { T tmp; ar >> tmp; t.push_back(tmp); } } };
template<typename Archive, typename K, typename V> class CAccess<Archive, map<K,V> > //模板偏特化,实现map容器的序列化和反序列化 { public: static void serialize(Archive& ar, map<K,V>& t) //调用序列化或反序列化函数,在编译期动态判断决定 { CFreeInvoke<Archive, map<K,V> >::invoke(ar, t); } static void marshall(Archive& ar, const map<K,V>& t) //序列化 { int len = t.size(); ar << len << " "; typename map<K,V>::const_iterator iter; for (iter = t.begin(); iter != t.end(); ++iter) ar << iter->first << " " << iter->second << " "; } static void demarshall(Archive& ar, map<K,V>& t) //反序列化 { int len = 0; ar >> len; t.clear(); for (int i = 0; i < len; i++) { K key; V val; ar >> key >> val; t[key] = val; } } };
class CTextSerialize //序列化和协议实现类 { public: typedef bool_plt<true> is_marshall; //该类定义为序列化类 typedef bool_plt<false> is_demarshall; CTextSerialize(ostream& o):os(o){}
template<typename T> void serialize(const T& t, bool_plt<false>& b) //基础类型序列化模板函数 { os << t << " "; } template<typename T> void serialize(const T& t, bool_plt<true>& b) //类类型序列化模板函数 { CAccess<CTextSerialize, T>::serialize(*this, const_cast<T&>(t)); } template<typename T> CTextSerialize& operator<<(const T& t) { bool_plt<is_class<T>::value> type; //type在编译期确定,T是否是类类型 serialize(t, type); return *this; }
template<typename T> CTextSerialize& operator&(const T& t) { bool_plt<is_class<T>::value> type; //type在编译期确定,T是否是类类型 serialize(t, type); return *this; } private: ostream& os; };
class CTextDeserialize //反序列化和协议实现类 { public: typedef bool_plt<false> is_marshall; typedef bool_plt<true> is_demarshall; //该类定义为反序列化类 CTextDeserialize(istream& i):is(i){}
template<typename T> void deserialize(T& t, bool_plt<false>& b) //基础类型反序列化模板函数 { is >> t; } template<typename T> void deserialize(T& t, bool_plt<true>& b) //类类型反序列化模板函数 { CAccess<CTextDeserialize, T>::serialize(*this, t); } template<typename T> CTextDeserialize& operator>>(T& t) { bool_plt<is_class<T>::value> type; //type在编译期确定,T是否是类类型 deserialize(t, type); return *this; }
template<typename T> CTextDeserialize& operator&(T& t) { bool_plt<is_class<T>::value> type; //type在编译期确定,T是否是类类型 deserialize(t, type); return *this; } private: istream& is; };
enum EName{}; struct SData{};
class CData //支持序列化和反序列化的类实现 { private: //待序列化的成员变量 uint32_t ver; int i; bool b; long l; double d; string s; vector<string> vecStr; map<int, string> mapInfo;
public: CData():ver(0),i(0),b(false),l(0),d(0){} //数据初始化 void init(uint32_t ver, int i, bool b, long l, double d, string s, string arr[], int len) { this->ver = ver; this->i = i; this->b = b; this->l = l; this->d = d; this->s = s; this->vecStr.assign(arr, arr + len); for (int j = 0; j < len; j++) mapInfo[j] = arr[j]; } template<typename Archive> //模板多态,Archive可以实现多种序列化协议 Archive& serialize(Archive& ar) //序列化和反序列化都调用这个模板函数 { ar & ver; ar & i; ar & b; ar & l; ar & d; ar & s; ar & vecStr; ar & mapInfo;
return ar; }
string tostr(void) //便于类对象打印输出 { stringstream ss; ss << " ver " << ver << " int:" << i << " bool:" << (true==b ? "true" : "false") << " long:" << l << " double:" << d << " string:" << s; int len = vecStr.size(); ss << " vector:" << len << " "; for (int j = 0; j < len; j++) ss << vecStr[j] << " "; ss << " map:" << len << " "; for (int j = 0; j < len; j++) ss << j << " " << mapInfo[j] << " ";
return ss.str(); } };
int main(void) { {//将数据存入流中,将数据从流中取出;空格做为数据分隔符,简单的数据存储格式 stringstream ss;
int a = 1; double b = 2.1; string c = "abc"; ss << a << " " << b << " " << c; int A = 0; double B = 0; string C; ss >> A >> B >> C;
cout << ss.str() << endl; cout << A << " " << B << " " << C << endl << endl; }
{//使用模板方式,在编译期判断数据类型,是否是类类型 cout << is_class<int>::value << endl;//该代码块都是基础数据类型 cout << is_class<double>::value << endl; cout << is_class<EName>::value << endl; cout << is_class<string>::value << endl;
cout << is_class<CData>::value << endl;//该代码块都是类类型 cout << is_class<SData>::value << endl; cout << is_class<vector<int> >::value << endl << endl; }
{//序列化和反序列化基础数据类型 int a = 1; double b = 2.1; string c = "abc";
std::ostringstream os; CTextSerialize oSer(os); oSer << a << b << c; cout << a << " " << b << " " << c << endl;
int A = 0; double B = 0; string C;
std::istringstream is(os.str()); CTextDeserialize iDeser(is); iDeser >> A >> B >> C; cout << A << " " << B << " " << C << endl << endl; }
{//序列化和反序列化类类型 string arr[] = {"3a", "2b", "1c"}; int len = sizeof(arr)/sizeof(arr[0]);//C++内存布局与C语言兼容 CData oData; oData.init(0, 11, true, 222, 3.30, "string", arr, len);
std::ostringstream os; CTextSerialize oSer(os); oSer << oData; cout << "oData:" << oData.tostr() << endl;
CData iData; std::istringstream is(os.str()); CTextDeserialize iDeser(is); iDeser >> iData; cout << "iData:" << iData.tostr() << endl; }
return 0; } |
注:代码没有达到产品级别的质量。
代码编译后,运行结果:
1 2.1 abc 1 2.1 abc
0 0 0 0 1 1 1
1 2.1 abc 1 2.1 abc
oData: ver 0 int:11 bool:true long:222 double:3.3 string:string vector:3 3a 2b 1c map:3 0 3a 1 2b 2 1c iData: ver 0 int:11 bool:true long:222 double:3.3 string:string vector:3 3a 2b 1c map:3 0 3a 1 2b 2 1c |
参考文献
[1] Boost序列化官网,https://www.boost.org/doc/libs/1_68_0/libs/serialization/doc/index.html
[2] Boost 1.67.0源代码,https://www.boost.org/users/history/version_1_67_0.html
[3] 维基百科,https://en.wikipedia.org/wiki/Serialization