有没有办法解决平面缓冲区联合中 255 种类型的限制?
Is there a way to workaround the limit of 255 types in a flatbuffers union?
我正在使用平面缓冲区序列化来自 sql table 的行。我有一个 Statement.fbs 将语句定义为 Insert、Update、Delete 等。该语句有一个成员 "Row",它是所有 sql table 类型的联合。但是,我有超过 255 tables,并且在使用 flatc 编译时出现此错误:
$ ~/flatbuffers/flatc --cpp -o gen Statement.fbs
error: /home/jkl/fbtest/allobjects.fbs:773: 18: error: enum value does not fit [0; 255]
我查看了 flatbuffers 代码,发现自动为联合类型创建了一个枚举,并且该枚举的基础类型是 uint8_t。
我没有看到任何更改此行为的选项。
我可以创建一个枚举来处理我的所有 table,方法是在我的平面缓冲区模式文件中将基础类型指定为 uint16。
语句架构:
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
allobjects Row 联合有点大,无法包含在此处。
union Row {
TypeA,
TypeB,
TypeC,
Etc,
...
}
我想这是联合类型应该只使用一个字节的平面缓冲区的设计决策。我可以接受,但我真的想要一个解决方法。
遗憾的是这是一个设计错误,目前还没有解决方法。将其修复为可配置是可能的,但考虑到依赖它作为一个字节的语言端口的数量,这将是一项相当大的工作。参见例如这里:https://github.com/google/flatbuffers/issues/4209
是的,多个联合是一个笨拙的解决方法。
另一种方法是将类型定义为枚举。但是,现在您遇到的问题是您没有类型安全的方法来存储 table。这可以通过 "nested flatbuffer" 来实现,即将联合值存储为字节向量,一旦你检查了枚举,你就可以用正确的类型廉价地调用 GetRoot。
另一个选项可能是枚举 + 联合,如果唯一类型记录的数量小于 256。例如,您可能有多个行类型,即使它们具有不同的名称,但它们的内容只是一个字符串, 因此它们可以合并为联合类型。
另一个 hack 可能是声明一个 table RowBaseClass {}
或其他什么,这将是字段的类型,但你永远不会真正实例化这个 table。然后根据您使用的语言来回转换为该类型以存储实际的 table。
联合的 255 个限制的嵌套缓冲区解决方案非常简单。
allobjects.fbs:
namespace Database;
table Garbage {
gid:ulong;
type:string;
weight:uint;
}
... many more ...
Statement.fbs:
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
// suppose this enum holds the > 255 Row types
enum TableKind : uint16 { Unknown = 0, Garbage, Etc... }
// this is the "union", but with a type enum beyond ubyte size
table Row {
kind:TableKind;
// this payload will be the nested flatbuffer
payload:[ubyte];
}
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
main.c:
#include <iostream>
#include "Statement_generated.h"
void encodeInsertGarbage(unsigned long gid,
const std::string& type,
unsigned int weight,
std::vector<uint8_t>& retbuf)
{
flatbuffers::FlatBufferBuilder fbb;
// create Garbage flatbuffer
// I used the "Direct" version so I didn't have to create a flatbuffer string object
auto garbage = Database::CreateGarbageDirect(fbb, gid, type.c_str(), weight);
fbb.Finish(garbage);
// make [ubyte] from encoded "Garbage" object
auto payload = fbb.CreateVector(fbb.GetBufferPointer(), fbb.GetSize());
// make the generic Row homebrewed union
auto obj = Database::CreateRow(fbb, Database::TableKind_Garbage, payload);
fbb.Finish(obj);
// create the Statement - 0 for "truncate" since that is not used for Insert
auto statement = Database::CreateStatement(fbb, Database::StatementKind_Insert, 0, obj);
fbb.Finish(statement);
// copy the resulting flatbuffer to output vector
// just for this test program, typically you write to a file or socket.
retbuf.assign(fbb.GetBufferPointer(), fbb.GetBufferPointer() + fbb.GetSize());
}
void decodeInsertGarbage(std::vector<uint8_t>& retbuf)
{
auto statement = Database::GetStatement(retbuf.data());
auto tableType = statement->row()->kind();
auto payload = statement->row()->payload();
// just using a simple "if" statement here, but a full solution
// could use an array of getters, indexed by TableKind, then
// wrap it up nice with a template function to cast the return type
// like rowGet<Garbage>(payload);
if (tableType == Database::TableKind_Garbage)
{
auto garbage = Database::GetGarbage(payload->Data());
std::cout << " gid: " << garbage->gid() << std::endl;
std::cout << " type: " << garbage->type()->c_str() << std::endl;
std::cout << " weight: " << garbage->weight() << std::endl;
}
}
int main()
{
std::vector<uint8_t> iobuf;
encodeInsertGarbage(0, "solo cups", 12, iobuf);
decodeInsertGarbage(iobuf);
return 0;
}
输出:
$ ./fbtest
gid: 0
type: solo cups
weight: 12
我正在使用平面缓冲区序列化来自 sql table 的行。我有一个 Statement.fbs 将语句定义为 Insert、Update、Delete 等。该语句有一个成员 "Row",它是所有 sql table 类型的联合。但是,我有超过 255 tables,并且在使用 flatc 编译时出现此错误:
$ ~/flatbuffers/flatc --cpp -o gen Statement.fbs
error: /home/jkl/fbtest/allobjects.fbs:773: 18: error: enum value does not fit [0; 255]
我查看了 flatbuffers 代码,发现自动为联合类型创建了一个枚举,并且该枚举的基础类型是 uint8_t。
我没有看到任何更改此行为的选项。
我可以创建一个枚举来处理我的所有 table,方法是在我的平面缓冲区模式文件中将基础类型指定为 uint16。
语句架构:
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
allobjects Row 联合有点大,无法包含在此处。
union Row {
TypeA,
TypeB,
TypeC,
Etc,
...
}
我想这是联合类型应该只使用一个字节的平面缓冲区的设计决策。我可以接受,但我真的想要一个解决方法。
遗憾的是这是一个设计错误,目前还没有解决方法。将其修复为可配置是可能的,但考虑到依赖它作为一个字节的语言端口的数量,这将是一项相当大的工作。参见例如这里:https://github.com/google/flatbuffers/issues/4209
是的,多个联合是一个笨拙的解决方法。
另一种方法是将类型定义为枚举。但是,现在您遇到的问题是您没有类型安全的方法来存储 table。这可以通过 "nested flatbuffer" 来实现,即将联合值存储为字节向量,一旦你检查了枚举,你就可以用正确的类型廉价地调用 GetRoot。
另一个选项可能是枚举 + 联合,如果唯一类型记录的数量小于 256。例如,您可能有多个行类型,即使它们具有不同的名称,但它们的内容只是一个字符串, 因此它们可以合并为联合类型。
另一个 hack 可能是声明一个 table RowBaseClass {}
或其他什么,这将是字段的类型,但你永远不会真正实例化这个 table。然后根据您使用的语言来回转换为该类型以存储实际的 table。
联合的 255 个限制的嵌套缓冲区解决方案非常简单。
allobjects.fbs:
namespace Database;
table Garbage {
gid:ulong;
type:string;
weight:uint;
}
... many more ...
Statement.fbs:
include "allobjects.fbs";
namespace Database;
enum StatementKind : byte { Unknown = 0, Insert, Update, Delete, Truncate }
// suppose this enum holds the > 255 Row types
enum TableKind : uint16 { Unknown = 0, Garbage, Etc... }
// this is the "union", but with a type enum beyond ubyte size
table Row {
kind:TableKind;
// this payload will be the nested flatbuffer
payload:[ubyte];
}
table Statement {
kind:StatementKind;
truncate:[TableKind];
row:Row;
}
root_type Statement;
main.c:
#include <iostream>
#include "Statement_generated.h"
void encodeInsertGarbage(unsigned long gid,
const std::string& type,
unsigned int weight,
std::vector<uint8_t>& retbuf)
{
flatbuffers::FlatBufferBuilder fbb;
// create Garbage flatbuffer
// I used the "Direct" version so I didn't have to create a flatbuffer string object
auto garbage = Database::CreateGarbageDirect(fbb, gid, type.c_str(), weight);
fbb.Finish(garbage);
// make [ubyte] from encoded "Garbage" object
auto payload = fbb.CreateVector(fbb.GetBufferPointer(), fbb.GetSize());
// make the generic Row homebrewed union
auto obj = Database::CreateRow(fbb, Database::TableKind_Garbage, payload);
fbb.Finish(obj);
// create the Statement - 0 for "truncate" since that is not used for Insert
auto statement = Database::CreateStatement(fbb, Database::StatementKind_Insert, 0, obj);
fbb.Finish(statement);
// copy the resulting flatbuffer to output vector
// just for this test program, typically you write to a file or socket.
retbuf.assign(fbb.GetBufferPointer(), fbb.GetBufferPointer() + fbb.GetSize());
}
void decodeInsertGarbage(std::vector<uint8_t>& retbuf)
{
auto statement = Database::GetStatement(retbuf.data());
auto tableType = statement->row()->kind();
auto payload = statement->row()->payload();
// just using a simple "if" statement here, but a full solution
// could use an array of getters, indexed by TableKind, then
// wrap it up nice with a template function to cast the return type
// like rowGet<Garbage>(payload);
if (tableType == Database::TableKind_Garbage)
{
auto garbage = Database::GetGarbage(payload->Data());
std::cout << " gid: " << garbage->gid() << std::endl;
std::cout << " type: " << garbage->type()->c_str() << std::endl;
std::cout << " weight: " << garbage->weight() << std::endl;
}
}
int main()
{
std::vector<uint8_t> iobuf;
encodeInsertGarbage(0, "solo cups", 12, iobuf);
decodeInsertGarbage(iobuf);
return 0;
}
输出:
$ ./fbtest
gid: 0
type: solo cups
weight: 12