2

My understanding is that root type is some legacy thing, it is needed only for two reasons:

  1. root_type name (name, not content) is used to create some objects, for example embed binary schema.
  2. some syntax sugar API is generated only for root type. Since it is just sugar, you don't have to use it.

Am I right or am I missing something?

So for example if I do:

table Foo { ... }
table Bar {...}
...

// empty root at the end to make flatbuffers happy
table Fbs {}
root_type Fbs

I named it Fbs so that generated schema looks nice: FbsBinarySchema. Another option would be to use file name, since it contains binary schema of full file, and thus MyFileBinarySchema makes sense.

wibotwi
  • 108
  • 6

1 Answers1

0

The root_type is very important for flatbuffers and is not a legacy thing.

It is used to know the type of the table referenced by the buffer, so it knows how to decode its contents.

If this is a FlatBuffer binary:

[32-bit offset to root type][... misc ...][root type][... misc ...]
             ^                            ^
             |                            |
This value has the numeric offset to  ----/

Currently in your schema, no Foo or Bar tables are accessible. You need to add them to your Fbs table:

table Fbs {
  my_field:Foo;
  my_other_field:Bar;
  collection_of_foos:[Foo];
}

So you should have a generated accessor:

const Fbs* GetFbs(uint8_t * buffer);

That allows you to get the root type/table. From there, all other things in the buffer are referenceable from the root type.

uint8_t* buffer = ... // from somewhere

const Fbs* my_fbs = GetFbs(buffer);

const Foo* my_foo = my_fbs.my_field();
Moop
  • 3,414
  • 2
  • 23
  • 37
  • Look like you did not understand my question. "GetFbs" - this is syntax sugar for GetRoot. And GetRoot accepts any table type: Foo, Bar. "Currently in your schema, no Foo or Bar tables are accessible." This makes no sense. Both Foo and Bar can be serialized and deserialized. All methods to do that are generated. When you serialize Foo then root_offset in binary will point to Foo. Of course that binary will contain only Foo. So when you deserialize you must know that incoming message is Foo. – wibotwi May 03 '23 at 06:10
  • Yes, GetRoot accepts any type, but that doesn't mean the backing binary is of that type. You could write `GetRoot(buffer)` and not use the generated syntactic sugar. But that is not the issue, if you have no root type (or empty in your case) no `Foo` or `Bar` will be accessible by anyone. – Moop May 03 '23 at 06:14
  • What do you mean by `no Foo or Bar will be accessible by anyone` ? They are accessible with `GetRoot` and `GetRoot`. It works, I use it in production code. Moreover several threads on stackoverflow say that `GetRoot` is correct method to access any table, regardless root or not. – wibotwi May 03 '23 at 06:42
  • Without a `root_type`, you don't know what's stored in the buffer. Yes, you could serialize a `Foo` or `Bar` but the reader doesn't know what's in it. You would have to pass that information out-of-band: "Hey, I am sending you this buffer, it stores a `Foo` at offset 0". The `root_type` communicates that for you, by encoding that information in the schema itself, which is shared by writer/readers. `no Foo or Bar will be accessible by anyone` I meant that (without outside information) a reader has no way to tell what's in the buffer and how to deserialize it. – Moop May 03 '23 at 07:08
  • But you have to know what you receive in any case. Say I have two fbs files. each of them has root type. And I am sending both messages to the client. The client has to communicate with me of "how to interpret incoming binary data". I have to tell him that. Saving root_type in fbs is just one of millions ways for communication. It is not a mandatory way. Another way could be: storing a comment on wiki page or even a comment inside fbs file. – wibotwi May 03 '23 at 07:26
  • This is off topic, but in your case with 2 fbs files, I would typically have a single root table and either a union/two fields to indicate the "sub message". That way the client always knows how to read a buffer, regardless of contents. They would deserialize the root_type first, then make a run-time decision on how to parse the rest of the content. No OOB comms required for this. Yes, you are free to communicate the contents however you want. But this is the main purpose of the `root_type` (there are others like JSON parsing, but that doesn't matter here). – Moop May 03 '23 at 07:30
  • Anyway. Can we agree that answer to my original question is: "Root type is an optional way to verbally communicate what messages are being passed in binary data. Having empty root type is OK for the cases where such communication is not needed". – wibotwi May 03 '23 at 07:37
  • Oh, I just looked up that you are "Lead Maintainer of Flatbuffers". Would be nice if you can patch `flatc` so it will generate embed binary schema when root_type is absent. In such case I would be able to remove that empty Fbs from all my fbs files :) – wibotwi May 03 '23 at 07:53
  • Can you file a bug on the repo for that? – Moop May 03 '23 at 16:01
  • Opened ticket: https://github.com/google/flatbuffers/issues/7931 – wibotwi May 04 '23 at 03:26