概述
本篇blog主要是給大家介紹FlatBuffers的相關的信息和用法,當我在了解的FlatBuffers時,國內還沒有一些相關的文章去介紹FlatBuffers,不得不翻牆去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。