序列化是一个常见的功能,与数据库、配置文件不同,序列化一般是以二进制方式存储的,不考虑肉眼可读性。
序列化的基本原理很简单,用同样的顺序写入写出即可。主要需要琢磨的是如何用尽可能简洁的方式来实现,客户代码需要添加的部分越少越好。
这是我写过的一个版本(测试代码也在里面):
- //Archive.h 二进制序列化
-
- #ifndef STD_ARCHIVE_H
- #define STD_ARCHIVE_H
-
-
- namespace ns__std_2
- {
- class CArchive
- {
- public:
- CBuffer m_buf;
-
- void StartArchive(char const * name)
- {
- m_buf.setSize(0);
- m_buf.AddData((void *)name,strlen(name)+1);
- }
- //对各种类型的操作
- template<typename T>
- CArchive & operator<<(T const & data)
- {
- data.Serialize(*this);
- return *this;
- }
- template<typename T>
- CArchive & Add(T const & data)
- {
- m_buf.AddData(&data,sizeof(T));
- return *this;
- }
- //对基本类型的特化,如果用到其它基本类型会导致“Serialize不支持”错误,需要在此增加
- template <long BUFSIZE >
- CArchive & operator<<(sstring
const & data) { m_buf.AddData(data.c_str(), data.size() + 1); return *this; } - CArchive & operator<<(char const * data) { m_buf.AddData(data, strlen(data) + 1); return *this; }
- CArchive & operator<<(char * data) { m_buf.AddData(data, strlen(data) + 1); return *this; }
- CArchive & operator<<(string const & data){ m_buf.AddData((void *)data.c_str(), data.size() + 1); return *this; }
- CArchive & operator<<(long data) { return Add(data); }
- CArchive & operator<<(int data) { return Add(data); }
- CArchive & operator<<(unsigned long data) { return Add(data); }
- CArchive & operator<<(unsigned int data) { return Add(data); }
- CArchive & operator<<(bool data) { return Add(data); }
- };
- class CUnArchive
- {
- private:
- CBuffer const * m_pBuf;
- string::size_type m_pos;
- bool m_isOK;
- public:
- operator bool ()const{return m_isOK;}
- long getPos()const { return m_pos; }
- bool StartUnArchive(char const * name, CBuffer const * source)
- {
- m_pBuf=source;
- m_pos=0;
- m_isOK=true;
-
- if(0!=strcmp(name,m_pBuf->data()))
- {
- m_isOK=false;
- }
- m_pos+=strlen(m_pBuf->data())+1;
- return operator bool ();
- }
-
- //对各种类型的操作
- template<typename T>
- CUnArchive & operator>>(T & data)
- {
- if (!m_isOK)return *this;
- data.UnSerialize(*this);
- return *this;
- }
- template<typename T>
- CUnArchive & Get(T & data)
- {
- if (m_pos >= m_pBuf->size())
- {
- thelog << "溢出 " << m_pBuf->size() << " " << m_pos << ende;
- m_isOK = false;
- return *this;
- }
- memcpy(&data, m_pBuf->data() + m_pos, sizeof(T));
- m_pos += sizeof(T);
- return *this;
- }
- //对基本类型的特化,如果用到其它基本类型会导致“UnSerialize不支持”错误,需要在此增加
- template <long BUFSIZE >
- CUnArchive & operator>>(sstring
& data) - {
- if (m_pos >= m_pBuf->size())
- {
- thelog << "溢出 " << m_pBuf->size() << " " << m_pos << ende;
- m_isOK = false;
- return *this;
- }
- data = m_pBuf->data() + m_pos;
- m_pos += strlen(m_pBuf->data() + m_pos) + 1;//必须用原始数据计算长度
- return *this;
- }
- CUnArchive & operator>>(string & data)
- {
- if (m_pos >= m_pBuf->size())
- {
- thelog << "溢出 " << m_pBuf->size() << " " << m_pos << ende;
- m_isOK = false;
- return *this;
- }
- data = m_pBuf->data() + m_pos;
- m_pos += strlen(m_pBuf->data() + m_pos) + 1;//必须用原始数据计算长度
- return *this;
- }
- CUnArchive & operator>>(long & data) { return Get(data); }
- CUnArchive & operator>>(int & data) { return Get(data); }
- CUnArchive & operator>>(unsigned long & data) { return Get(data); }
- CUnArchive & operator>>(unsigned int & data) { return Get(data); }
- CUnArchive & operator>>(bool & data) { return Get(data); }
- };
-
- class CArchiveTest
- {
- public:
- struct A
- {
- bool a;
- int b;
- long c;
- string d;
- sstring<16> f;
- void Serialize(CArchive & ar)const
- {
- ar << a << b << c << d << f;
- }
- void UnSerialize(CUnArchive & unar)
- {
- unar >> a >> b >> c >> d >> f;
- }
- };
- static int CArchiveTest_doTest(int argc,char ** argv)
- {
- CArchive ar;
- A tmp;
- tmp.a=true;
- tmp.b=1;
- tmp.c=2;
- tmp.d="3";
- tmp.f="4";
- ar.StartArchive("a");
- ar<
-
- CUnArchive unar;
- if(!unar.StartUnArchive("a",&ar.m_buf))return __LINE__;
- A tmp2;
- unar>>tmp2;
- thelog<
- thelog<
- thelog<
- thelog<
- thelog<
c_str()< - return 0;
- }
- };
- }
-
- #endif
代码里面用到的buffer类在这里:代码设计:C++ 一个保证带有结束符的缓冲区类(源码)-CSDN博客
这个类并没有直接写文件,只是把数据写到了buffer里。
这个类仍然需要对序列化和反序列化写两次代码,仅仅是操作运算符不同(>>和<<),仍然不是很理想,我后来在一个XML配置文件功能里用了这个办法:在类里面用成员变量“bool isSerialize;”来表示处理方向,这样代码就大幅简化,我想或许也可以用在这个类里面。
参考代码:
- //不要直接使用这类,使用CXmlArchive_Save和CXmlArchive_Load
- class CXmlArchive
- {
- private:
- bool m_bSave;
- TiXmlNode* m_pParentNode;
- TiXmlNode* AddOrGet(char const* name)
- {
- if (m_bSave)
- {
- return CmyXML::Add(m_pParentNode->ToElement(), name);
- }
- else
- {
- return CmyXML::Get(m_pParentNode->ToElement(), name);
- }
- }
- public:
- CXmlArchive(bool bSave, TiXmlNode* pParentNode) :m_bSave(bSave), m_pParentNode(pParentNode) {}
- bool isSave()const { return m_bSave; }
- //通用,必须实现XmlArchive
- template<typename T>
- CXmlArchive& operator()(char const* name, T& data)
- {
- CXmlArchive new_archive = *this;
- new_archive.m_pParentNode = AddOrGet(name);
- data.XmlArchive(new_archive);
- return *this;
- }
- //vector的实现
- template<typename T>
- CXmlArchive& operator()(char const* name, vector
& data) ; - //String的实现,如果包含中文或以x开头,转码为x开头的16进制
- CXmlArchive& operator()(char const* name, CString& data);
- CXmlArchive& operator()(char const* name, int& data)
- {
- if (m_bSave)
- {
- CmyXML::Add(m_pParentNode->ToElement(), name, data);
- }
- else
- {
- CmyXML::Get(m_pParentNode->ToElement(), name, data);
- }
- return *this;
- }
- CXmlArchive& operator()(char const* name, double& data)
- {
- if (m_bSave)
- {
- CmyXML::Add(m_pParentNode->ToElement(), name, data);
- }
- else
- {
- CmyXML::Get(m_pParentNode->ToElement(), name, data);
- }
- return *this;
- }
- CXmlArchive& operator()(char const* name, bool& data)
- {
- if (m_bSave)
- {
- CmyXML::Add(m_pParentNode->ToElement(), name, data);
- }
- else
- {
- CmyXML::Get(m_pParentNode->ToElement(), name, data);
- }
- return *this;
- }
- };
-
- class CXmlArchive_Save : public CXmlArchive
- {
- public:
- CXmlArchive_Save(int/*无意义,避免单参数构造函数*/, TiXmlNode* pParentNode) :CXmlArchive(true, pParentNode) {}
- };
- class CXmlArchive_Load : public CXmlArchive
- {
- public:
- CXmlArchive_Load(int/*无意义,避免单参数构造函数*/, TiXmlNode* pParentNode) :CXmlArchive(false, pParentNode) {}
- };
这个代码用起来是相当舒服的:
- //数据类里面增加这个即可,不用关心方向
- void XmlArchive(CXmlArchive& ar)
- {
- ar("ID", ID);
- ar("Name", Name);
- ar("Status", Status);
- }
-
- //保存的时候这样写,其中root是已经打开的tinyXML文档
- CXmlArchive_Save ar(0, root);
- ar("vList", vDevList);
-
(这里是结束)
-
相关阅读:
开源库_20210224
Git 创建分支、合并分支
2022年深度学习最新研究成果
关于Java上下转型
python打开浏览器并模拟搜索
LVS集群
Unity角色或摄像机移动和旋转的控制脚本
如何使用并查集解决朋友圈问题?
PAA介绍
2.2线性表的顺序表示-综合应用题(408统考真题10-14)
-
原文地址:https://blog.csdn.net/2301_77171572/article/details/133920644