Mutable FlatBuffers: in-place updates.

This commit contains the first step in providing mutable FlatBuffers,
non-const accessors and mutation functions for existing fields generated
from --gen-mutable.

Change-Id: Iebee3975f05c1001f8e22824725edeaa6d85fbee
Tested: on Linux.
Bug: 15777024
This commit is contained in:
Wouter van Oortmerssen
2015-04-27 16:25:06 -07:00
parent a8d6962ac2
commit 3ec5dddb00
10 changed files with 225 additions and 22 deletions

View File

@@ -51,6 +51,9 @@ be generated for each file processed:
- `--gen-includes` : Generate include statements for included schemas the
generated file depends on (C++).
- `--gen-mutable` : Generate additional non-const accessors for mutating
FlatBuffers in-place.
- `--proto`: Expect input files to be .proto files (protocol buffers).
Output the corresponding .fbs file.
Currently supports: `package`, `message`, `enum`.

View File

@@ -163,6 +163,55 @@ Similarly, we can access elements of the inventory array:
assert(inv->Get(9) == 9);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
### Mutating FlatBuffers
As you saw above, typically once you have created a FlatBuffer, it is
read-only from that moment on. There are however cases where you have just
received a FlatBuffer, and you'd like to modify something about it before
sending it on to another recipient. With the above functionality, you'd have
to generate an entirely new FlatBuffer, while tracking what you modify in your
own data structures. This is inconvenient.
For this reason FlatBuffers can also be mutated in-place. While this is great
for making small fixes to an existing buffer, you generally want to create
buffers from scratch whenever possible, since it is much more efficient and
the API is much more general purpose.
To get non-const accessors, invoke `flatc` with `--gen-mutable`.
Similar to the reading API above, you now can:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
auto monster = GetMutableMonster(buffer_pointer); // non-const
monster->mutate_hp(10); // Set table field.
monster->mutable_pos()->mutate_z(4); // Set struct field.
monster->mutable_inventory()->Mutate(0, 1); // Set vector element.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We use the somewhat verbose term `mutate` instead of `set` to indicate that
this is a special use case, not to be confused with the default way of
constructing FlatBuffer data.
After the above mutations, you can send on the FlatBuffer to a new recipient
without any further work!
Note that any `mutate_` functions on tables return a bool, which is false
if the field we're trying to set isn't present in the buffer. Fields are not
present if they weren't set, or even if they happen to be equal to the
default value. For example, in the creation code above we set the `mana` field
to `150`, which is the default value, so it was never stored in the buffer.
Trying to call mutate_mana() on such data will return false, and the value won't
actually be modified!
There's two ways around this. First, you can call `ForceDefaults()` on a
`FlatBufferBuilder` to force all fields you set to actually be written. This
of course increases the size of the buffer somewhat, but this may be
acceptable for a mutable buffer.
Alternatively, you can use mutation functions that are able to insert fields
and change the size of things. These functions are expensive however, since
they need to resize the buffer and create new data.
### Storing maps / dictionaries in a FlatBuffer
FlatBuffers doesn't support maps natively, but there is support to