Flatbuffers 向量中是否可能有间隙或空元素?
我需要传输一个(可变长度)项目列表,其中项目的位置是相关的。不过,项目可以省略,但现有项目仍必须位于正确的位置
假设
table Item {
value:string;
}
table List {
items:[Item];
}
root_type List;
void createBuffer()
{
auto fbb = ::flatbuffers::FlatBufferBuilder{};
auto vector = std::vector<::flatbuffers::Offset<Item>>{};
vector.push_back(CreateItem(fbb, fbb.CreateString("Test")));
// Adds "empty" item
vector.push_back({});
auto fVector = fbb.CreateVector(vector);
auto list = CreateList(fbb, fVector);
fbb.Finish(list);
}
这会在创建 FBB 向量的
fbb.CreateVector
方法中崩溃。
有没有办法在向量中添加“间隙”,而不修改
Item
表?
不,这是不可能的。虽然表字段允许不存在/为空,但所有向量元素必须具有指定的类型。
无法在模式中指定“可选”向量元素类型。
你最好的选择是联合类型的向量。联合将包含
Item
类型,并且每个联合还允许“none”值,相当于 null。不过,这需要更多的工作,因为您实际上将存储 2 个向量,一个类型和一个值。
正如评论者和@Aardappel 提到的,无法将“空”元素添加到向量中。我最终使用了向量联合的(新的?)功能。
但是,我找不到如何在 C++ 中使用它的描述(只有 C# 教程),所以这里有一个示例实现,供参考。
基本上,FlatBuffers 将写入 2 个向量,一个用于元素的类型,另一个用于实际元素
table Item {
value:string;
}
table Empty {
}
union OptionalItem {
Item,
Empty
}
table List {
items:[OptionalItem];
}
root_type List;
std::vector<uint8_t> createBuffer()
{
auto fbb = ::flatbuffers::FlatBufferBuilder{};
auto itemVector = std::vector<::flatbuffers::Offset<>>{};
auto typeVector = std::vector<OptionalItem>{};
itemVector.push_back(CreateItem(fbb, fbb.CreateString("Test")).Union());
typeVector.push_back(OptionalItem::Item);
// Adds "empty" item
itemVector.push_back(CreateEmpty(fbb).Union());
typeVector.push_back(OptionalItem::Empty);
auto fbbItems = fbb.CreateVector(itemVector);
auto fbbTypes = fbb.CreateVector(typeVector);
auto list = CreateList(fbb, fbbTypes, fbbItems);
fbb.Finish(list);
return std::vector<uint8_t>(fbb.GetBufferPointer(), fbb.GetBufferPointer() + fbb.GetSize());
}
void readBuffer(std::vector<uint8_t> buffer)
{
auto list = GetList(buffer.data());
for (int i = 0; i < list->items()->size(); ++i) {
if (list->items_type()->Get(i) == OptionalItem::Item) {
auto item = reinterpret_cast<const Item*>(list->items()->Get(i));
std::cout << i << ": " << item->value()->c_str() << std::endl;
}
else if (list->items_type()->Get(i) == OptionalItem::Empty) {
std::cout << i << " is empty" << std::endl;
}
}
}