flatbuffer使用教程

一、定义scheme

Tables

FlatBuffers 中定义对象的主要方式,由名称和字段列表组成。每个字段都有一个名称、一个类型和一个可选的默认值。
如果架构中未指定默认值,则标量类型的默认值将为 0 ,其他类型的默认值将为 null 。

  • 限制
  1. 只能在表定义的末尾在架构中添加新字段,如果您希望灵活地在架构中使用任何顺序的字段,您可以手动分配 id。
  2. 无法从架构中删除不再使用的字段,但您可以简单地停止将它们写入数据中以获得几乎相同的效果。此外,您可以将它们标记为 deprecated
  3. 如果您可以更改代码,则可以更改字段名称和表名称,直到您也在那里重命名它们为止。

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