flatbuffer使用教程
一、定义scheme
Tables
FlatBuffers 中定义对象的主要方式,由名称和字段列表组成。每个字段都有一个名称、一个类型和一个可选的默认值。
如果架构中未指定默认值,则标量类型的默认值将为 0 ,其他类型的默认值将为 null 。
- 限制
- 只能在表定义的末尾在架构中添加新字段,如果您希望灵活地在架构中使用任何顺序的字段,您可以手动分配 id。
- 无法从架构中删除不再使用的字段,但您可以简单地停止将它们写入数据中以获得几乎相同的效果。此外,您可以将它们标记为 deprecated
- 如果您可以更改代码,则可以更改字段名称和表名称,直到您也在那里重命名它们为止。
Structs
与表类似,只是现在没有任何字段是可选的(因此也没有默认值),并且不能添加或弃用字段。结构体只能包含标量或其他结构体。将此用于您非常确定不会进行任何更改的简单对象。
结构比表使用更少的内存,并且访问速度更快
Types
-
内置类型
- 8 bit: byte (int8), ubyte (uint8), bool
- 16 bit: short (int16), ushort (uint16)
- 32 bit: int (int32), uint (uint32), float (float32)
- 64 bit: long (int64), ulong (uint64), double (float64)
其中括号中的类型名称是别名
-
内置非标量类型:
-
[type]
-
string
只能保存 UTF-8 或 7 位 ASCII。对于其他文本编码或一般二进制数据,请使用向量[byte]
一旦使用字段,您就无法更改它们的类型,但相同大小的数据除外
Array
数组是固定长度元素集合的便捷简写。
- 比如说:
struct Vec3 {
x:float;
y:float;
z:float;
}
- 可以替换为:
struct Vec3 {
v:[float:3];
}
默认值、可选值和必需值
枚举
定义一系列命名常量,每个常量都有一个给定值,或者比前一个值增加 1。默认的第一个值为 0 。
通常,枚举值只能被添加,不能被删除(枚举不会被弃用)。这需要代码通过处理未知的枚举值来处理自身的前向兼容性。
工会(Unions)
联合与枚举共享许多属性,但您使用表名称而不是常量的新名称。然后,您可以声明一个联合字段,它可以保存对任何这些类型的引用,此外还会生成一个后缀为 _type 的字段,该字段保存相应的枚举值,让您知道要转换哪种类型到运行时。
namespace
仅C++和java代码生成的时候有这个概念。
其它
- Root Type
这声明了您认为是序列化数据的根表。这对于解析不包含对象类型信息的 JSON 数据尤其重要。
二、在C++中使用
schema
namespace dshengChen;
enum Color : byte { Red = 1, Green, Blue }
struct Son {
x:float;
y:float;
z:float;
}
table Monster {
son:Son;
name:string;
age:uint8;
color:Color = Blue;
}
root_type Monster;
使用指令: 将schema编译为c++版本和python版本的代码。
flatc --cpp -o output_folder_path schema_path
flatc --python -o output_folder_path schema_path
序列化
FlatBuffers 的核心是内存效率,这就是为什么它的基本 API 是围绕使用尽可能少的内存而编写的。这确实使 API 变得更加笨拙(需要对所有数据进行预序构建,并使突变变得更加困难)。
void serialization_data(string user_name, uint8_t user_age)
{
flatbuffers::FlatBufferBuilder builder;
auto name = builder.CreateString(user_name);
dshengChen::Son son(0.1, 0.2, 0.3);
auto m = dshengChen::CreateMonster(builder, &son, name, user_age, dshengChen::Color_Green);
builder.Finish(m);
// 获取 FlatBuffer 数据指针和大小
const uint8_t *buffer = builder.GetBufferPointer();
size_t size = builder.GetSize();
std::ofstream outfile(filePath, std::ios::binary);
std::vector<char> data(size);
std::memcpy(data.data(), buffer, size);
outfile.write(data.data(), size);
outfile.close();
}
反序列化
void deserialization_data()
{
ifstream in_file(filePath, std::ios::binary);
if (in_file.is_open())
{
in_file.seekg(0, std::ios::end);
std::streamsize fileSize = in_file.tellg();
in_file.seekg(0, std::ios::beg);
// 读取文件内容到缓冲区
std::vector<char> buffer(fileSize);
in_file.read(buffer.data(), fileSize);
using namespace dshengChen;
auto parsed_person = GetMonster(buffer.data());
int age = parsed_person->age();
Color c = parsed_person->color();
cout << "\t age is: " << age;
cout << "\t name is: " << parsed_person->name()->c_str();
cout << "\t color is: " << (int)c << endl;
}
// 关闭文件
in_file.close();
}
c++ flatbuffer的cmake工程
cmake_minimum_required(VERSION 3.6)
project(py11_t VERSION 0.1.0 LANGUAGES C CXX)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 17)
include(CPack)
include(FetchContent)
FetchContent_Declare(
flatbuffers
GIT_REPOSITORY https://github.com/google/flatbuffers.git
GIT_TAG v23.5.26
)
FetchContent_MakeAvailable(flatbuffers)
include_directories("./generated")
add_executable(run_e test.cpp)
target_link_libraries(run_e PRIVATE flatbuffers)
# flatc --cpp ./example.fbs -o generated