c++对象的序列化与反序列化的解决方案----flatbuffers的使用
概述
本篇blog主要是给大家介绍FlatBuffers的相关的信息和用法,当我在了解的FlatBuffers时,国内还没有一些相关的文章去介绍FlatBuffers,不得不FQ去google相关的用法,然后理解并应用到自己的代码中,学习的时间成本很高。所以就花了点时间整理一份具体的用法,提供给大家一个参考。
简介
一、什么是FlatBuffers?
FlatBuffers是一个开源的、跨平台的、高效的、提供了C++/Java接口的序列化工具库。它是Google专门为游戏开发或其他性能敏感的应用程序需求而创建。尤其更适用于移动平台,这些平台上内存大小及带宽相比桌面系统都是受限的,而应用程序比如游戏又有更高的性能要求。它将序列化数据存储在缓存中,这些数据既可以存储在文件中,又可以通过网络原样传输,而不需要任何解析开销。
代码托管主页: https://github.com/google/flatbuffers;
项目介绍主页: http://google.github.io/flatbuffers/index.html;
二、FlatBuffers用途有哪些?
1、对C++代码的序列化与反序列化:①写本地缓存,方便读取。②用于网络数据发送。
2、将xml、json文件转换成二进制文件,大大缩减加载文件时间
用法
关于FlatBuffers的用法,我下面还是通过代码向大家讲解,这样更直观,更容易理解。
class Point { float x; float y; }; class Node { std::string name; Point position; }; class Layer : public Node { Node* friend; std::vector<Node*> children; std::vector<Point *> transform; };
1、使用前的准备
首先构建一个schema文件。schema文件主要是记录了我们所要用的对象的成员信息。
//>>>>>>>>> schema begin <<<<<<<<<<<
namespace Layer; table Point_Fb { x:float;
y:float;
} table Node_Fb { name:string;
position:Point_Fb; }
table Layer_Fb
{
super:Node_Fb;
friend:Node_Fb;
children:[Node_Fb];
transform:[Point_Fb];
} root_type Layer_Fb;
//>>>>>>>>> schema end <<<<<<<<<<<
到这里我们的schema文件已经写完了,然后保存为
Layer_Fb.fbs文件。
然后下载google的flatbuffers的开源代码编译flatc.cpp得到flatc可执行文件,然后运行
flatc -c -o ./ ./Layer_Fb.fbs 生成一个Layer_Fb_generated.h的头文件,加到项目中。到这里,我们的准备工作就做完了。
注:
1、flatbuffers的类型有很多我就没有一一列举了,大家可以在flatbuffers的文档里看到。
2、schema文件中的除了table还有struct。区别就在于able是Flatbuffers中用来定义对象的主要方式,和struct最大的区别在于:它的每个字段都是可选的,而struct的所有成员都是required。
table除了成员名称和类型之外,还可以给成员一个默认值,如果不显式指定,则默认为0(或空)。struct不能定义scalar成员,比如说string类型的成员。在生成C++代码时,struct的成员顺序
会保持和IDL的定义顺序一致,如果有必要对齐,生成器会自动生成用于对齐的额外成员。
如果没有Layer中没有std::vector<Point*> tranform,那么这里我们的Point的定义可以是struct Point_Fb,因为我没有找到flatbuffers里面如何使用结构体数组的方法。(如果各位有找到还望不吝赐教)
2、具体使用方法
1、序列化
这里我有个对象就是auto layer = new Layer();如何序列化呢?
我们就是要创建一个layer_Fb的对象,这个就是Layer对象对应的flatbuffers的对象,他包含了Layer对象的所有的信息。
#include "Layer_Fb_generated.h" flatbuffers::FlatBufferBuilder builder_data; auto position_fb = CreatePoint_Fb(builder_data,layer.position.x,layer.position.y); auto super_fb = CreateNode_Fb(builder_data,builder_data.CreateString(layer.name),&position_fb); auto friend_fb = ...; std::vector<flatbuffers::Offset<Node_Fb>> Node_fbList; for (auto child : layer.children) { auto position_fb = CreatePoint_Fb(builder_data,child.position.x,child.position.y); auto child_fb = CreateNode_Fb(builder_data,builder_data.CreateString(child.name),&position_fb); Node_fbList.push_back(child_fb); } std::vector<flatbuffers::Offset<Point_Fb>> transformList; for (auto point : layer.transform) { auto position_fb = CreatePoint_Fb(builder_data,layer.position.x,position.y); transformList.push_back(position_fb); } auto layer_fb = CreateLayer_Fb(builder_data,position_fb,super_fb,friend_fb,Node_fbList,transformList); // auto layer_fb = CreateLayer_Fb(builder_data, // position_fb, // super_fb, // friend_fb, // Node_fbList.size() == 0 ? 0 : Node_fbList, //这样可以减少多余存储空间 // transformList); builder_data.Finish(texture); // (char *)builder_data.GetBufferPointer(), builder_data.GetSize() 取得转换后的二进制文件。保存到本地或者用于网络传输
2、反序列化
auto layer_fb = flatbuffers::GetRoot<Layer_Fb>(builder_data.GetBufferPointer());// layer_fb->super/*schema 中的对象的名字*/(); layer_fb->friend(); layer_fb->children(); layer_fb->transfrom();
创建layer对象,对其一个个赋值就可以了。
注:
这里并不是只能对schame中的root_type才能序列化,例如:你想只对Node_fb进行序列化,你就可以在得到node_fb对象的时候直接builder.finish(node_fb),返回利用
flatbuffers::GetRoot<Node_Fb/*类型不要错咯*/>(builder_data.GetBufferPointer());方法一样可以
3、扩展
那么如何对xml、json文件进行序列化呢?
我的做法就是把xml、json文件解析成c++对象,然后序列化,保存到本地。然后就用保存的文件进行读取、反序列化操作。我对比过两中方式的读取效率。很明显读取flatbuffers文件后进行反序列化要比xml、json速度快6~10倍!但看这个值可能没感觉,当你解析一个xml可能用0.01s,但是解析flatbuffers文件你只要0.003秒。文件一多对比就会出来了。尤其是在手机游戏上的时候,启动和界面切换加载就会明显快好多。(如果你从事cocos2dx开发的话,你可以研究研究cocos的csb文件,其实就是一个flatbuffers文件。)
谢谢各位的阅读!
各位有什么疑问可以直接在我的blog下留言或者是发送的我的个人邮箱relvin@qq.com。