【原創】自己手寫實現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; } < |