热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

boostany实现万能容器_基于泛型编程的序列化实现方法

写在前面序列化是一个转储-恢复的操作过程,即支持将一个对象转储到临时缓冲或者永久文件中和恢复临时缓冲或者永久文件中的内容到一个对象中等操作,其目的是可以
写在前面

序列化是一个转储-恢复的操作过程,即支持将一个对象转储到临时缓冲或者永久文件中和恢复临时缓冲或者永久文件中的内容到一个对象中等操作,其目的是可以在不同的应用程序之间共享和传输数据,以达到跨应用程序、跨语言和跨平台的解耦,以及当应用程序在客户现场发生异常或者崩溃时可以即时保存数据结构各内容的值到文件中,并在发回给开发者时再恢复数据结构各内容的值以协助分析和定位原因。

泛型编程是一个对具有相同功能的不同类型的抽象实现过程,比如STL的源码实现,其支持在编译期由编译器自动推导具体类型并生成实现代码,同时依据具体类型的特定性质或者优化需要支持使用特化或者偏特化及模板元编程等特性进行具体实现。

Hello World

#include int main(int argc, char* argv[]){ std::cout <<"Hello World!" <

泛型编程其实就在我们身边&#xff0c;我们经常使用的std和stl命名空间中的函数和类很多都是泛型编程实现的&#xff0c;如上述代码中的std::cout即是模板类std::basic_ostream的一种特化

namespace std{ typedef basic_ostream ostream;}从C&#43;&#43;的标准输入输出开始

除了上述提到的std::cout和std::basic_ostream外&#xff0c;C&#43;&#43;还提供了各种形式的输入输出模板类&#xff0c;如std::basic_istream&#xff0c; std::basic_ifstream&#xff0c;std::basic_ofstream&#xff0c; std::basic_istringstream&#xff0c;std::basic_ostringstream等等&#xff0c;其主要实现了内建类型(built-in)的输入输出接口&#xff0c;比如对于Hello World可直接使用于字符串&#xff0c;然而对于自定义类型的输入输出&#xff0c;则需要重载实现操作符>>和<

class MyClip{ bool mValid; int mIn; int mOut; std::string mFilePath;};

如使用下面的方式则会出现一连串的编译错误

MyClip clip;std::cout <

错误内容大致都是一些clip不支持<

为了解决编译错误&#xff0c;我们则需要将类MyClip支持输入输出操作符>>和<

inline std::istream& operator>>(std::istream& st, MyClip& clip){ st >> clip.mValid; st >> clip.mIn >> clip.mOut; st >> clip.mFilePath; return st;}inline std::ostream& operator<

为了能正常访问类对象的私有成员变量&#xff0c;我们还需要在自定义类型里面增加序列化和反序列化的友元函数(回忆一下这里为何必须使用友元函数而不能直接重载操作符>>和<)&#xff0c;如

friend std::istream& operator>>(std::istream& st, MyClip& clip);friend std::ostream& operator<

这种序列化的实现方法是非常直观而且容易理解的&#xff0c;但缺陷是对于大型的项目开发中&#xff0c;由于自定义类型的数量较多&#xff0c;可能达到成千上万个甚至更多时&#xff0c;对于每个类型我们则需要实现2个函数&#xff0c;一个是序列化转储数据&#xff0c;另一个则是反序列化恢复数据&#xff0c;不仅仅增加了开发实现的代码数量&#xff0c;如果后期一旦对部分类的成员变量有所修改&#xff0c;则需要同时修改这2个函数。

同时考虑到更复杂的自定义类型&#xff0c;比如含有继承关系和自定义类型的成员变量

class MyVideo : public MyClip{ std::list mFilters;};

上述代码需要转储-恢复类MyVideo的对象内容时&#xff0c;事情会变得更复杂些&#xff0c;因为还需要转储-恢复基类&#xff0c;同时成员变量使用了STL模板容器list与自定义类&#39;MyFilter&#96;的结合&#xff0c;这种情况也需要自己去定义转储-恢复的实现方式。

针对以上疑问&#xff0c;有没有一种方法能减少我们代码修改的工作量&#xff0c;同时又易于理解和维护呢&#xff1f;

Boost序列化库

对于使用C&#43;&#43;标准输入输出的方法遇到的问题&#xff0c;好在Boost提供了一种良好的解决方式&#xff0c;则是将所有类型的转储-恢复操作抽象到一个函数中&#xff0c;易于理解&#xff0c;如对于上述类型&#xff0c;只需要将上述的2个友元函数替换为下面的一个友元函数

template friend void serialize(Archive&, MyClip&, unsigned int const);

友元函数的实现类似下面的样子

templatevoid serialize(A &ar, MyClip &clip, unsigned int const ver){ ar & BOOST_SERIALIZATION_NVP(clip.mValid); ar & BOOST_SERIALIZATION_NVP(clip.mIn); ar & BOOST_SERIALIZATION_NVP(clip.mOut); ar & BOOST_SERIALIZATION_NVP(clip.mFilePath);}

其中BOOST_SERIALIZATION_NVP是Boost内部定义的一个宏&#xff0c;其主要作用是对各个变量进行打包。

转储-恢复的使用则直接作用于操作符>>和<

// storeMyClip clip;······std::ostringstream ostr;boost::archive::text_oarchive oa(ostr);oa <> clip;

这里使用的std::istringstream和std::ostringstream即是分别从字符串流中恢复数据以及将类对象的数据转储到字符串流中。

对于类MyFilter和MyVideo则使用相同的方式&#xff0c;即分别增加一个友元模板函数serialize的实现即可&#xff0c;至于std::list模板类&#xff0c;boost已经帮我们实现了。

这时我们发现&#xff0c;对于每一个定义的类&#xff0c;我们需要做的仅仅是在类内部声明一个友元模板函数&#xff0c;同时类外部实现这个模板函数即可&#xff0c;对于后期类的成员变量的修改&#xff0c;如增加、删除或者重命名成员变量&#xff0c;也仅仅是修改一个函数即可。

Boost序列化库已经足够完美了&#xff0c;但故事并未结束&#xff01;

在用于端上开发时&#xff0c;我们发现引用Boost序列化库遇到了几个挑战

  • 端上的编译资料很少&#xff0c;官方对端上编译的资料基本没有&#xff0c;在切换不同的版本进行编译时经常会遇到各种奇怪的编译错误问题
  • Boost在不同的C&#43;&#43;开发标准之间兼容性不够好&#xff0c;尤其是使用libc&#43;&#43;标准进行编译链接时遇到的问题较多
  • Boost增加了端上发行包的体积
  • Boost每次序列化都会增加序列化库及版本号等私有头信息&#xff0c;反序列化时再重新解析&#xff0c;降低了部分场景下的使用性能
基于泛型编程的序列化实现方法

为了解决使用Boost遇到的这些问题&#xff0c;我们觉得有必要重新实现序列化库&#xff0c;以剥离对Boost的依赖&#xff0c;同时能满足如下要求

  • 由于现有工程大量使用了Boost序列化库&#xff0c;因此兼容现有的代码以及开发者的习惯是首要目标
  • 尽量使得代码修改和重构的工作量最小
  • 兼容不同的C&#43;&#43;开发标准
  • 提供比Boost序列化库更高的性能
  • 降低端上发行包的体积

为了兼容现有使用Boost的代码以及保持当前开发者的习惯&#xff0c;同时使用代码修改的重构的工作量最小&#xff0c;我们应该保留模板函数serialize&#xff0c;同时对于模板函数内部的实现&#xff0c;为了提高效率也不需要对各成员变量重新打包&#xff0c;即直接使用如下定义

#define BOOST_SERIALIZATION_NVP(value) value

对于转储-恢复的接口调用&#xff0c;仍然延续目前的调用方式&#xff0c;只是将输入输出类修改为

alivc::text_oarchive oa(ostr);alivc::text_iarchive ia(istr);

好了&#xff0c;到此为止&#xff0c;序列化库对外的接口工作已经做好&#xff0c;剩下的就是内部的事情&#xff0c;应该如何重新设计和实现序列化库的内部框架才能满足要求呢&#xff1f;

先来看一下当前的设计架构的处理流程图

d5deddb5a1d28cf6f246e88ee8082401.png

比如对于转储类text_oarchive&#xff0c;其支持的接口必须包括

explicit text_oarchive(std::ostream& ost, unsigned int version &#61; 0);template text_oarchive& operator

开发者调用操作符函数<

template text_oarchive& operator<

当开始对具体类型的各个成员进行操作时&#xff0c;这时需要进行判断&#xff0c;如果此成员变量的类型已经是内建类型&#xff0c;则直接进行序列化&#xff0c;如果是自定义类型&#xff0c;则需要重新回调到对应类型的模板函数serialize中

template text_oarchive& operator&(T& v){ basic_save::invoke(*this, v, mversion); return *this;}

上述代码中的basic_save::invoke则会在编译期完成模板类型推导并选择直接对内建类型进行转储还是重新回调到成员变量对应类型的serialize函数继续重复上述过程。

由于内建类型数量有限&#xff0c;因此这里我们选择使模板类basic_save的默认行为为回调到相应类型的serialize函数中

template struct basic_load_save{ template static void invoke(A& ar, T& v, unsigned int version) { serialize(ar, v, version); }};template struct basic_save : public basic_load_save::value>{};

这时会发现上述代码的模板参数多了一个参数E&#xff0c;这里主要是需要对枚举类型进行特殊处理&#xff0c;使用偏特化的实现如下

template struct basic_load_save{ template static void invoke(A& ar, T& v, unsigned int version) { int tmp &#61; v; ar & tmp; v &#61; (T)tmp; }};

到这里我们已经完成了重载操作符&的默认行为&#xff0c;即是不断进行回溯到相应的成员变量的类型中的模板函数serialize中&#xff0c;但对于碰到内建模型时&#xff0c;我们则需要让这个回溯过程停止&#xff0c;比如对于int类型

template struct basic_pod_save{ template static void invoke(A& ar, T const& v, unsigned int) { ar.template save(v); }};template <>struct basic_save : public basic_pod_save{};

这里对于int类型&#xff0c;则直接转储整数值到输出流中&#xff0c;此时text_oarchive则还需要增加一个终极转储函数

template void save(T const& v){ most <

这里我们发现&#xff0c;在save成员函数中&#xff0c;我们已经将具体的成员变量的值输出到流中了。

对于其它的内建类型&#xff0c;则使用相同的方式处理&#xff0c;要以参考C&#43;&#43; std::basic_ostream的源码实现。

相应的&#xff0c;对于恢复操作的text_iarchive的操作流程如下图

4b9a0c5c12251b358567abd4a30ab5ff.png
测试结果

我们对使用Boost以及重新实现的序列化库进行了对比测试&#xff0c;其结果如下

代码修改的重构的工作非常小&#xff0c;只需要删除Boost的相关头文件&#xff0c;以及将boost相关命名空间替换为alivc&#xff0c;BOOST_SERIALIZATION_FUNCTION以及BOOST_SERIALIZATION_NVP的宏替换Android端下的发行包体积大概减少了500KB目前的消息处理框架中&#xff0c;处理一次消息的平均时间由100us降低到了25us代码实现约300行&#xff0c;更轻量级未来还能做什么

由于当前项目的原因&#xff0c;重新实现的序列化还没有支持转储-恢复指针所指向的内存数据&#xff0c;但当前的设计框架已经考虑了这种拓展性&#xff0c;未来会考虑支持。

总结泛型编程能够大幅提高开发效率&#xff0c;尤其是在代码重用方面能发挥其优势&#xff0c;同时由于其类型推导及生成代码均在编译期完成&#xff0c;并不会降低性能序列化对于需要进行转储-恢复的解耦处理以及协助定位异常和崩溃的原因分析具有重要作用利用C&#43;&#43;及模板自身的语言特性优势&#xff0c;结合合理的架构设计&#xff0c;即易于拓展又能尽量避免过度设计参考资料https://www.ibm.com/developerworks/cn/aix/library/au-boostserialization/

作者&#xff1a;lifesider




推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
author-avatar
驴友团的新家处_273
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有