设计思路

在与多个系统进行网络交互时,序列化是不可缺少的技术。编写一个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

转载于:https://www.cnblogs.com/churen/p/10013742.html

【原创】 Boost序列化自己手写实现简易版相关推荐

  1. 肝一波 ~ 手写一个简易版的Mybatis,带你深入领略它的魅力!

    零.准备工作 <dependencies><dependency><groupId>mysql</groupId><artifactId>m ...

  2. 手写一个简易版本的RPC

    前言 在1024程序员节前夕,学习技术是对节日最好的庆祝. 手写一个简易版的RPC,可以把模糊抽象的概念具像化,属于落地层面了. 1. RPC基本原理 RPC原理 2. 四个版本的迭代 注:api表示 ...

  3. 手写一个简易bundler打包工具带你了解Webpack原理

    用原生js手写一个简易的打包工具bundler

  4. 纯手写SpringFramework-完结版(原创)

    个人简介 作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门. 文章目录 个人简介 纯手写SpringFrame ...

  5. 自己写个简易版 PicGo

    自己写个简易版 PicGo 1.Why not PicGo? 不得不说,我被 PicGo 坑惨了,写了这么久的笔记,用了 PicGo 的一键上传,图片顺序全乱了... 我可真是老倒霉蛋了...我还是自 ...

  6. javascript实现图片轮播_手撸一个简易版轮播图(上)

    手撸一个简易版轮播图 实现原理,通过控制 swiper-warpper 容器的定位来达到切换图片的效果. 页面布局 简易版轮播图 < > 页面样式 .container{width: 60 ...

  7. 【c++】手写笔记扫描版

    c++手写笔记扫描版 c++基础 类与对象 数据的共享与保护 数组.指针与字符串 进阶与派生 多态 模板与群体数据 泛型函数设计与c++标准模板 流类库与输入输出 异常处理 3月份境外确诊病例数增多 ...

  8. 手写 springIoc 注解版 ,实现@Service (beng),@Resource (依赖注入)

    手写springIoc 注解版 代码demo https://pan.baidu.com/s/1jyvLMDrg_bfpKmhtrTTZSQ 提取码:5ju1 代码目录结构 1.pom.xml < ...

  9. 从零手写实现简易版MMKV(一)

    概述 MMKV是支持多平台的高性能键值对持久化存储组件,其核心原理是利用mmap内存映射文件,关于它的详细介绍和更多原理参看MMKV开源git地址. 从零开始手写(其实是抄写-.-!)简易版MMKV( ...

最新文章

  1. 星辰变鸿蒙武器,星辰变手游亲手打造极品光武 成就炼器宗师
  2. c语言编写系统服务程序,C语言Windows服务程序编写-ServiceMain
  3. CSS:响应式下的折叠菜单(条纹式)
  4. Windows server 2003共享文件夹问题
  5. SRIO学习(六)——Direct I/O 操作(一)
  6. 【告别信】三年了,是该离开了!
  7. PHP 进程的实现与管理
  8. TMS320C6678上电配置和FPGA复位DSP
  9. 一文带你了解企业上云数据分析首选产品Quick BI
  10. 【BZOJ2728】[HNOI2012]与非 并查集+数位DP
  11. python数据类型和数据运算
  12. Mac下安装LNMP(Nginx+PHP5.6)环境
  13. pycon视频_观看6个PyCon 2015社区相关视频
  14. 妙用TurboMail企业通讯平台,重要邮件不再躲猫猫
  15. php模拟QQ登录获得skey码,PHP模拟QQ网页版授权登陆
  16. macbook linux 双显卡,网友支招:苹果笔记本也能双显卡切换
  17. ios修改apn的插件_iPhone手机APN修改方案
  18. 天创速盈:拼多多商家提升投产比有什么技巧?
  19. 人生苦短,用Python爬取微博大V
  20. 自己写的粗糙的Excel数据驱动Http接口测试框架(一)

热门文章

  1. 动态加载并获取usercontrol生成的html
  2. 分裂对象模型和TclCL(2)
  3. eclipse默认项目部署路径(.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps) 改为自己的tomcat真实路径方法
  4. 多线程bug处理记录
  5. maven打包报错找不到符号,由于找不到类中方法的解决思路
  6. 帆软报表插件开发之fine-decision中的LogInOutEventProvider扩展
  7. nginx启动只有master没有worker_深入探索Nginx工作原理
  8. c语言解三元一次方程组_在R里面对三元一次方程求解
  9. html 空格占位符_HTML常用英文单词,快来背单词吧
  10. 【文献阅读】Perceptual Generative Adversarial Networks for Small Object Detection –CVPR-2017