FlatBuffers C++ 如何使用 object-based API 构建与修改数据?
2026/9/13 3:24:17 网站建设 项目流程

FlatBuffers C++ 如何使用 object-based API 构建与修改数据?

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

FlatBuffers 的 C++ 基础 API 围绕内存效率设计,所有数据必须预先构建(pre-order construction),且原地修改(mutation)比较困难。当效率不是首要考虑时,flatc可以用--gen-object-api生成一套 object-based API:它能把 FlatBuffer unpack/pack 到普通 C++ 对象和标准 STL 容器,让构建、访问和修改数据都像操作普通类实例一样直接。本文说明如何生成这套代码、用它从零构建数据、修改已有 buffer 中的数据,并验证结果。适用前提:你已写好.fbsschema(写法见 schema 文档),并能构建出flatc编译器(构建方法见 building 文档)。

准备:构建 flatc

FlatBuffers 用 CMake 构建flatc,它是仓库中构建出的二进制之一。Unix 环境下的最短路径(来自 tutorial):

cmake -G "Unix Makefiles" make flatc

Windows 上使用cmake -G "Visual Studio 17 2022"msbuild.exe FlatBuffers.sln,更多环境见 building 文档。部分语言也通过各自的包管理器提供预构建的flatc

生成的 C++ 头文件依赖flatbuffers/flatbuffers.h(位于include/flatbuffers),这个目录需要在你的编译头文件搜索路径中。

用 --gen-object-api 生成 object-based API 代码

在普通--cpp生成参数上追加--gen-object-api

flatc --cpp --gen-object-api monster.fbs

这会生成如monster_generated.h的头文件,其中除基础 API 外,还会为每个 table 生成对应的对象类(例如Monstertable 生成MonsterT)。对 flatc 文档中该选项的说明是:这套 API 在对象构建与修改上更方便,代价是效率(对象分配),"Recommended only to be used if other options are insufficient"——即基础 API 不能满足构建/修改需求时再启用它。

在程序中包含运行时库和生成代码:

#include "flatbuffers/flatbuffers.h" // C++ 运行时库 #include "monster_generated.h" // flatc 生成

从零构建数据

object-based API 的对象类就是普通 C++ 类:字符串成员是std::string,vector 成员是标准容器,table/struct 成员因在 buffer 中可缺省,默认表示为std::unique_ptr。构建一个 buffer 的最短路径:

  1. 声明对象并像普通类实例一样赋值;
  2. 用生成代码的静态Pack函数把对象序列化进FlatBufferBuilder
  3. 调用Finish完成 buffer。
// Autogenerated class from table Monster. MonsterT monsterobj; monsterobj.name = "MyMonster"; // std::string,直接赋值 monsterobj.hp = 80; // Serialize into new flatbuffer. FlatBufferBuilder fbb; fbb.Finish(Monster::Pack(fbb, &monsterobj));

fbb.GetBufferPointer()fbb.GetSize()随后可以按基础 API 的方式导出 buffer。

修改已有 buffer 中的数据

修改既有数据时,先用生成的根访问器取出对象,调用UnPackTo反序列化成对象,直接修改成员,再用Pack序列化到一个新的flatbuffer(来自 C++ 语言指南的 "Object based API" 一节):

// Autogenerated class from table Monster. MonsterT monsterobj; // Deserialize from buffer into object. GetMonster(flatbuffer)->UnPackTo(&monsterobj); // Update object directly like a C++ class instance. cout << monsterobj.name; // This is now a std::string! monsterobj.name = "Bob"; // Change the name. // Serialize into new flatbuffer. FlatBufferBuilder fbb; fbb.Finish(Monster::Pack(fbb, &monsterobj));

这里的GetMonster是生成代码中针对root_type提供的访问器,flatbuffer指向已读入内存的原始 buffer 起点。整个流程不需要手工处理 offset 顺序——这是相对基础 API 的主要收益。

验证构建与修改结果

文档给出的验证手段分三种:

  1. 直接读字段:对象成员就是 STL 类型,打印或断言即可,如上面示例中cout << monsterobj.name
  2. 对象比较(可选生成)flatc--gen-compare选项会为 object-based API 类型生成operator==,可生成后直接比较两个MonsterT。仓库测试 tests/test.cpp 的EqualOperatorTest演示了用法:字段相同则b == atrue,修改mana后比较为false,改回再相等。
  3. 不可信 buffer 先校验:生成的访问器不校验 offset,格式错误的 buffer 可能导致崩溃。若数据来自网络等不可信来源,UnPackTo之前先用每个根类型生成的校验函数,例如对Monster
Verifier verifier(buf, len); bool ok = VerifyMonsterBuffer(verifier);

ok为 true 时 buffer 可安全读取。文档同时说明该校验比完整遍历快,且调试模式下可作为额外保险。

可选:调整指针类型、字符串类型与 schema 属性

flatc 文档和 C++ 语言指南还给出若干针对 object-based API 的生成选项,按需要选用:

  • --cpp-ptr-type T:设置 object API 的指针类型,默认std::unique_ptr。可改成任意智能指针my_ptr<T>,或写naked得到裸T *——裸指针不管理内存,生命周期需自行管理。单字段可用cpp_ptr_type属性覆盖,值default_ptr_type表示跟随--cpp-ptr-type
  • --cpp-str-type T:设置字符串类型,默认std::string。类型必须支持T::c_str()T::length()T::empty(),且默认要从std::string构造;--cpp-str-flex-ctorcpp_str_flex_ctor属性可改为以(const char *, size_t)构造自定义类型(该字符数组不保证以 NULL 结尾,须用传入的 size 判断结尾)。
  • --object-prefix/--object-suffix:自定义 object-based API 类名前缀/后缀。
  • --force-empty/--force-empty-vectors:从 object API 表示序列化时,强制字符串/vector 输出为空而非 null。

schema 层面(写在.fbs里)与 object-based API 代码生成相关的属性:

  • native_inline(字段上):把原本用unique_ptr包装的成员改为直接使用类型,避免可空指针。
  • native_default("value")(字段上):对native_inline成员,把指定值原样写入类构造函数的初始化列表。
  • native_custom_alloc("custom_allocator")(table/struct 上):unpack 时所有 NativeTable 及 table 中的std::vector都使用该分配器,可用于对象池等加速场景。要求分配器在包含flatbuffers.h之前定义,文档给出的最小示例是继承std::allocator<T>并实现allocate/deallocate/rebind的模板结构体(见 cpp.md 的 Minimal Example)。
  • native_type("type")(struct 上):用你自己的 C++ 类型替代生成的XxxT类,例如为Vec2提供带operator+/length()vector2。此时必须自行在flatbuffers命名空间提供Vec2 Pack(const vector2& obj)vector2 UnPack(const Vec2& obj)两个函数。
  • native_type_pack_name("name")(配合native_type):同一 native 类型复用于多个 struct 时,用该后缀区分Pack/UnPack函数名,避免编译错误。
  • native_include("path")(文件级):向生成代码顶部直接添加一条#include,用于引入native_type的外部头文件。

已知限制

  • 效率代价--gen-object-api的官方定位是"其他选项不足时再用",它引入对象分配,不适合对分配敏感的序列化主路径。
  • force_align不一定生效:object API 下该属性可能不被遵守,具体取决于new所用 allocator 的对齐。
  • 线程:读取 FlatBuffer 是只读的,多线程安全;但创建(Pack经由FlatBufferBuilder)不是线程安全的,推荐不要让多个线程共享同一个FlatBufferBuilder实例。
  • 修改产生新 buffer:object-based API 的修改路径是"unpack 到对象、修改、Pack 进新 buffer",文档未提供对原 buffer 的原地修改能力;如需原地修改基础 API buffer,flatc另有--gen-mutable选项生成非 const 访问器,属于基础 API 范畴,可另行参考 flatc 文档。

生成代码后可参考仓库中的实际示例继续深入:tests/test.cpp 中的对象 API 用法,以及 C++ 语言指南 中 object-based API 一节的完整属性说明。

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询