How to fill Vector<Offset<T>> after CreateUninitializedVector

311 views
Skip to first unread message

Patrick Frants

unread,
Sep 24, 2020, 5:05:08 AM9/24/20
to FlatBuffers
I want to avoid the intermediate std::vector required by CreateVector(const std::vector<T> &v). Therefore I thought I'd use CreateUninitializedVector instead, but I am having trouble filling the buffer correctly.

The use case differs from the examples I found, because the elements are of type Offset. From the sources I concluded I need to apply ReferTo() and then EndianScalar() to my elements before assigning them to the buffer. The following is not an exact copy from my code, but it shows the problem.

Vector<Offset<MyType>> SaveElements(const std::vector<MyAppType>& elements)
{
  Offset<MyType>* pbegin = nullptr;
  auto offset = builder_.CreateUninitializedVector(nr_elements, sizeof(Offset<MyType>), (uint8_t**)&pbegin);
  
  auto* pcurrent = pbegin;
  for ( const auto& element : elements )
  {
    Offset<MyType> element_offset = SaveElement(element);

    // How do I correctly write an Offset in the uninitialized buffer?
    *pcurrent++ = EndianScalar(builder_.ReferTo(element_offset));
  }
  return offset;
}

Any help is greatly appreciated.

Patrick

Patrick Frants

unread,
Sep 24, 2020, 10:52:08 AM9/24/20
to FlatBuffers
Corrected the code to not use ReferTo() but instead calculate the offset relative to the location where the offset is stored.

Of course that did not fix it. By design offsets can only refer to "objects" which have already been serialized, so they are "to-the-right" in the buffer. There can be no negative offsets (uoffset_t!) anywhere.

It seems my goal is not achievable and I do need the intermediate std::vector, just like CreateVector(const std::vector<T> &v).

Patrick

Wouter van Oortmerssen

unread,
Sep 24, 2020, 12:52:18 PM9/24/20
to Patrick Frants, FlatBuffers
I assume you have profiled and found the extra vector to be a performance bottleneck? 
Yes, you need some form of intermediate storage, sadly. You could try and re-use an existing std::vector though, or use alloca() to use stack memory, etc.

--
You received this message because you are subscribed to the Google Groups "FlatBuffers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flatbuffers...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/flatbuffers/28401f45-cf16-416f-99e5-22d6be31067cn%40googlegroups.com.

Patrick Frants

unread,
Sep 24, 2020, 2:42:41 PM9/24/20
to FlatBuffers
Just want to get it right the first time around and the extra std::vector caught my eye. In this use case I can reuse a vector, so it's not that bad. Big advantage is that the code stays simple this way compared to using CreateUninitializedVector and calculating the offset relative to the address where it's stored. And then the endianess.

Much happier with reusing the std::vector

Thanks!
Message has been deleted

Wouter van Oortmerssen

unread,
Oct 30, 2020, 12:24:41 PM10/30/20
to Paul K., FlatBuffers
Like any use of `CreateUninitializedVector` with offsets doesn't make a whole lot of sense, no, so we could add an explicit assert for that. Then again, this is a very specialized "you must know what you're doing" kind of function.

On Fri, Oct 30, 2020 at 3:11 AM Paul K. <pkoby...@gmail.com> wrote:
Should `CreateUninitializedVector(size_t len, T **buf)` fail `AssertScalarT` with `Offset<SomeFlatbuffersTable>` as `T`?
As reinterpret_cast from `Offset<T>` to `uoffset_t` seems correct, maybe, we need partial specialization for T=Offset<U>> ?


четверг, 24 сентября 2020 г. в 21:42:41 UTC+3, Patrick Frants:
Reply all
Reply to author
Forward
0 new messages