Union of struct with C++ full reflection

508 views
Skip to first unread message

Yuchao Song

unread,
Nov 6, 2021, 3:24:51 PM11/6/21
to FlatBuffers
Hi there,

I have a .fbs schema like below:

struct A {
  a: float32;
}

struct B {
  b: float32;
}

union ABUnionType {
  A,
  B
}

table MyConfig {
  instance: ABUnionType;
}

root_type MyConfig;

--------------------------------

I'd like to use C++ full reflection to inspect / update one field (say the "a" in struct A, from a concrete flatbuffer with struct A stored in the instance). From the bfbs file, I did something as follows (trying to following the ReflectionTest pattern):

const reflection::Schema& schema = *reflection::GetSchema(bfbsfile.c_str());

const reflection::Object& root_table = *schema.root_table();
auto objects = schema.objects();
auto fields = root_table.fields();

const reflection::Field& instance_field_ptr = *fields->LookupByKey("instance");
assert(instance_field_ptr.type()->base_type() == reflection::Union);

// concrete instance from flatbuffer
const flatbuffers::Table& root = *flatbuffers::GetAnyRoot(my_flat_buffer);

const flatbuffers::Table* instanceTable = flatbuffers::GetFieldT(root, instance_field_ptr);

auto field_a = instance_struct_fields->LookupByKey("a");

const reflection::Object& unionType =
      flatbuffers::GetUnionType(schema, root_table, instance_field_ptr, root);

auto field_a_ptr = unionType.fields()->LookupByKey("a");
assert(field_a_ptr);

--------------------------------

Now I'm stuck at this point, doing:
auto instance = flatbuffers::GetFieldF<float>(*instanceTable, *field_a_ptr);
will crash with EXC_BAD_ACCESS, as I guess instanceTable is describing the union, rather than the struct.

Any mechanism to get an "instanceStruct" of type "flatbuffers::Table*" pointing to the concrete struct A, so I can do something like:

flatbuffers::Table* instanceStruct = ???;
auto instance = flatbuffers::GetFieldF<float>(*instanceStruct, *field_a_ptr);

to its field "a" or even update its value?

Thanks.

Wouter van Oortmerssen

unread,
Nov 8, 2021, 2:02:07 PM11/8/21
to Yuchao Song, FlatBuffers
Structs and tables are very different things, so ` const flatbuffers::Table* instanceTable = flatbuffers::GetFieldT(root, instance_field_ptr);` is most certainly not going to do the right thing.

Structs as members of unions is a more recent feature, so it is entirely possible this is not supported yet. We'd need a GetFieldStruct or similar that returns a Struct *.

--
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/87a28f3c-abac-484a-b404-c8806dc4c4a4n%40googlegroups.com.

Yuchao Song

unread,
Nov 9, 2021, 12:29:03 PM11/9/21
to FlatBuffers
Thanks for the insights.

If adding a Union type to the check in:
--------------------------------

// Get a field, if you know it's a struct.
inline const Struct *GetFieldStruct(const Table &table,
                                    const reflection::Field &field) {
  // TODO: This does NOT check if the field is a table or struct, but we'd need
  // access to the schema to check the is_struct flag.
  FLATBUFFERS_ASSERT(field.type()->base_type() == reflection::Obj ||
                     field.type()->base_type() == reflection::Union); // <- local patch
  return table.GetStruct<const Struct *>(field.offset());
}

--------------------------------
Then:

const flatbuffers::Struct* instanceStruct = flatbuffers::GetFieldStruct(root, instance_field_ptr);

const reflection::Object& unionType = flatbuffers::GetUnionType(schema, root_table, instance_field_ptr, root);

for (auto const& unionField : *unionType.fields()) {
  cout << unionField->name()->c_str() << endl;
  auto temp_instance = instanceStruct->GetField<float>(unionField->offset());
  cout << temp_instance << endl;
}

seems the instance read from the offset is 4 bytes ahead of the expected buffer location. Any recommended way to address this properly (even on client side temporarily is fine)? Thanks.

Wouter van Oortmerssen

unread,
Nov 11, 2021, 12:34:55 PM11/11/21
to Yuchao Song, FlatBuffers
A struct directly in the table is inline (the computed location in the table directly holds the struct), whereas a union field are uniformly an offset to the union element, regardless of wether its a table/struct/string. So you'd need to load an offset from the location and do one indirection.

Yuchao Song

unread,
Nov 11, 2021, 1:54:00 PM11/11/21
to FlatBuffers
Thanks for the answer. Also got some hint from the mini reflection parsing code.
Reply all
Reply to author
Forward
0 new messages