c++serializationflatbuffers

Serialize a vector of tables without creating a temporary vector of offsets


Using flatbuffers v2.0.8, I'm trying to serialize a vector of tables without creating a temporary std::vector of offsets.

This would be especially useful when:

I tried using CreateUninitializedVector() to allocate space for said vector inside the builder, and fill it later, but it doesn't work: the flatbuffer doesn't seem to be correctly serialized.

Here's my code:

namespace protocol;

table Foo {
    id: string;
}

table Message {
    foos: [Foo];
}
#include <flatbuffers/flatbuffers.h>
#include <iostream>
#include "protocol_generated.h"

void deserialize(flatbuffers::FlatBufferBuilder& builder) {
    if (auto verifier = flatbuffers::Verifier(builder.GetBufferPointer(), builder.GetSize());
            verifier.VerifyBuffer<protocol::Message>())
        std::cout << "protocol::Message looks correctly serialized\n";
    else
        std::cout << "Couldn't verify buffer to be of type protocol::Message\n";
}

int main() {
    std::vector<std::string_view> ids = {"toto", "titi", "tata", "tutu"};
    {
        // This works, but needs a temporary std::vector
        flatbuffers::FlatBufferBuilder builder;
        std::vector<flatbuffers::Offset<protocol::Foo>> vec;

        for (auto const id: ids)
            vec.push_back(protocol::CreateFoo(builder, builder.CreateString(id)));
        auto const offset = protocol::CreateMessage(
                builder,
                builder.CreateVector(vec)
        );
        builder.Finish(offset);

        deserialize(builder);
    }
    {
        // This is NOT OK
        flatbuffers::FlatBufferBuilder builder;
        flatbuffers::Offset<protocol::Foo>* buf = nullptr;
        auto const offset = protocol::CreateMessage(
                builder,
                builder.CreateUninitializedVector(ids.size(), sizeof(flatbuffers::Offset<flatbuffers::String>), reinterpret_cast<uint8_t **>(&buf))
        );
        for (size_t i = 0; i < ids.size(); ++i)
            buf[i] = protocol::CreateFoo(builder, builder.CreateString(ids[i]));
        builder.Finish(offset);

        deserialize(builder);
    }
    return 0;
}

I get the following output:

protocol::Message looks correctly serialized
Couldn't verify buffer to be of type protocol::Message

I fear what I'm trying to do might not be possible.


Solution

  • You are missing a CreateFoo in the second code.

    Even then, poking the resulting offset into buf like that will not work, since all Create functions return an offset relative to the end of the buffer, and what you need to store is an offset relative to the offset itself. Which won't work here, because that offset would be negative, and it is an unsigned offset.

    FlatBuffers has a hard requirement on offsets from parent -> child being forward in the buffer, so this can't work.

    Thus, you must find a piece of memory somewhere where you can temporarily store those offsets.