設計思路
在與多個系統進行網絡交互時,序列化是不可缺少的技術。編寫一個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