mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-29 14:42:01 +00:00
Move some C specifics out of tutorial and clarify platform support
This commit is contained in:
@@ -19,32 +19,194 @@ project.
|
|||||||
- The C Builder Interface (advanced) <https://github.com/dvidelabs/flatcc/blob/master/doc/builder.md>
|
- The C Builder Interface (advanced) <https://github.com/dvidelabs/flatcc/blob/master/doc/builder.md>
|
||||||
|
|
||||||
|
|
||||||
|
## Supported Platforms
|
||||||
|
|
||||||
|
Ubuntu and OS-X are regularly tested during releases. Centos 7.1
|
||||||
|
has also been tested. Cross compilation to little-endian ARM has been
|
||||||
|
reported to work with warnings.
|
||||||
|
|
||||||
|
Windows has not been tested. The `include/flatcc/portable` library is
|
||||||
|
intended to abstract platform differences, including Windows. User
|
||||||
|
feedback and patches are welcome.
|
||||||
|
|
||||||
|
Big endian platforms have not been tested and may contain bugs, but care
|
||||||
|
has been taken to provide support for it.
|
||||||
|
|
||||||
|
|
||||||
|
## Modular Object Creation
|
||||||
|
|
||||||
|
In the tutorial we used the call `Monster_create_as_root` to create the
|
||||||
|
root buffer object since this is easier in simple use cases. Sometimes
|
||||||
|
we need more modularity so we can reuse a function to create nested
|
||||||
|
tables and root tables the same way. For this we need the
|
||||||
|
`flatcc_builder_buffer_create_call`. It is best to keep `flatcc_builder`
|
||||||
|
calls isolated at the top driver level, so we get:
|
||||||
|
|
||||||
|
<div class="language-c">
|
||||||
|
~~~{.c}
|
||||||
|
ns(Monster_ref_t) create_orc(flatcc_builder_t *B)
|
||||||
|
{
|
||||||
|
// ... same as in the tutorial.
|
||||||
|
return s(Monster_create(B, ...));
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_monster_buffer()
|
||||||
|
{
|
||||||
|
uint8_t *buf;
|
||||||
|
size_t size;
|
||||||
|
flatcc_builder_t builder, *B;
|
||||||
|
|
||||||
|
// Initialize the builder object.
|
||||||
|
B = &builder;
|
||||||
|
flatcc_builder_init(B);
|
||||||
|
// Only use `buffer_create` without `create/start/end_as_root`.
|
||||||
|
flatcc_builder_buffer_create(create_orc(B));
|
||||||
|
// Allocate and copy buffer to user memory.
|
||||||
|
buf = flatcc_builder_finalize_buffer(B, &size);
|
||||||
|
// ... write the buffer to disk or network, or something.
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
flatcc_builder_clear(B);
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
</div>
|
||||||
|
|
||||||
|
The same principle applies with `start/end` vs `start/end_as_root` in
|
||||||
|
the top-down approach.
|
||||||
|
|
||||||
|
|
||||||
|
## Top Down Example
|
||||||
|
|
||||||
|
The tutorial uses a bottom up approach. In C it is also possible to use
|
||||||
|
a top-down approach by starting and ending objects nested within each
|
||||||
|
other. In the tutorial there is no deep nesting, so the difference is
|
||||||
|
limited, but it shows the idea:
|
||||||
|
|
||||||
|
<div class="language-c">
|
||||||
|
<br>
|
||||||
|
~~~{.c}
|
||||||
|
uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
size_t treasure_count = c_vec_len(treasure);
|
||||||
|
ns(Weapon_ref_t) axe;
|
||||||
|
|
||||||
|
// NOTE: if we use end_as_root, we MUST also start as root.
|
||||||
|
ns(Monster_start_as_root(B));
|
||||||
|
ns(Monster_pos_create(B, 1.0f, 2.0f, 3.0f));
|
||||||
|
ns(Monster_hp_add(B, 300));
|
||||||
|
ns(Monster_mana_add(B, 150));
|
||||||
|
// We use create_str instead of add because we have no existing string reference.
|
||||||
|
ns(Monster_name_create_str(B, "Orc"));
|
||||||
|
// Again we use create because we no existing vector object, only a C-array.
|
||||||
|
ns(Monster_inventory_create(B, treasure, treasure_count));
|
||||||
|
ns(Monster_color_add(B, ns(Color_Red)));
|
||||||
|
if (1) {
|
||||||
|
ns(Monster_weapons_start(B));
|
||||||
|
ns(Monster_weapons_push_create(B, flatbuffers_string_create_str(B, "Sword"), 3));
|
||||||
|
// We reuse the axe object later. Note that we dereference a pointer
|
||||||
|
// because push always returns a short-term pointer to the stored element.
|
||||||
|
// We could also have created the axe object first and simply pushed it.
|
||||||
|
axe = *ns(Monster_weapons_push_create(B, flatbuffers_string_create_str(B, "Axe"), 5));
|
||||||
|
ns(Monster_weapons_end(B));
|
||||||
|
} else {
|
||||||
|
// We can have more control with the table elements added to a vector:
|
||||||
|
//
|
||||||
|
ns(Monster_weapons_start(B));
|
||||||
|
ns(Monster_weapons_push_start(B));
|
||||||
|
ns(Weapon_name_create_str(B, "Sword"));
|
||||||
|
ns(Weapon_damage_add(B, 3));
|
||||||
|
ns(Monster_weapons_push_end(B));
|
||||||
|
ns(Monster_weapons_push_start(B));
|
||||||
|
ns(Monster_weapons_push_start(B));
|
||||||
|
ns(Weapon_name_create_str(B, "Axe"));
|
||||||
|
ns(Weapon_damage_add(B, 5));
|
||||||
|
axe = *ns(Monster_weapons_push_end(B));
|
||||||
|
ns(Monster_weapons_end(B));
|
||||||
|
}
|
||||||
|
// Unions can get their type by using a type-specific add/create/start method.
|
||||||
|
ns(Monster_equipped_Weapon_add(B, axe));
|
||||||
|
|
||||||
|
ns(Monster_end_as_root(B));
|
||||||
|
~~~
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
## Basic Reflection
|
## Basic Reflection
|
||||||
|
|
||||||
The C-API does support reading binary schema (.bfbs)
|
The C-API does support reading binary schema (.bfbs)
|
||||||
files via code generated from the `reflection.fbs` schema, and an
|
files via code generated from the `reflection.fbs` schema, and an
|
||||||
[example usage](https://github.com/dvidelabs/flatcc/tree/master/samples/reflection)
|
[example usage](https://github.com/dvidelabs/flatcc/tree/master/samples/reflection)
|
||||||
shows how to use this. The schema files are pre-generated
|
shows how to use this. The reflection schema files are pre-generated
|
||||||
in the [runtime distribution](https://github.com/dvidelabs/flatcc/tree/master/include/flatcc/reflection).
|
in the [runtime distribution](https://github.com/dvidelabs/flatcc/tree/master/include/flatcc/reflection).
|
||||||
|
|
||||||
|
|
||||||
## Mutating Reflection
|
## Mutations and Reflection
|
||||||
|
|
||||||
The C-API does not support mutating reflection like C++ does.
|
The C-API does not support mutating reflection like C++ does, nor does
|
||||||
|
the reader interface support mutating scalars (and it is generally
|
||||||
|
unsafe to do so even after verification).
|
||||||
|
|
||||||
Although the following isn't reflection, it is possible to create new
|
The generated reader interface supports sorting vectors in-place after
|
||||||
buffers using complex objects from existing buffers as source. This can
|
casting them to a mutating type because it is not practical to do so
|
||||||
be very efficient due to direct copy semantics without endian conversion or
|
while building a buffer. This is covered in the builder documentation.
|
||||||
temporary stack allocation.
|
The reflection example makes use of this feature to look up objects by
|
||||||
|
name.
|
||||||
|
|
||||||
|
It is possible to build new buffers using complex objects from existing
|
||||||
|
buffers as source. This can be very efficient due to direct copy
|
||||||
|
semantics without endian conversion or temporary stack allocation.
|
||||||
|
|
||||||
Scalars, structs and strings can be used as source, as well vectors of
|
Scalars, structs and strings can be used as source, as well vectors of
|
||||||
these.
|
these.
|
||||||
|
|
||||||
It is currently not possible to use an existing table or vector of table
|
It is currently not possible to use an existing table or vector of table
|
||||||
as source, but it would be possible to add support for this at some
|
as source, but it would be possible to add support for this at some
|
||||||
point. Vectors of strings
|
point.
|
||||||
|
|
||||||
|
|
||||||
|
## Namespaces
|
||||||
|
|
||||||
|
The `FLATBUFFERS_WRAP_NAMESPACE` approach used in the tutorial is convenient
|
||||||
|
when each function has a very long namespace prefix. But it isn't always
|
||||||
|
we the best approach. If the namespace is absent, or very simple and
|
||||||
|
informative, we might as well use the prefix directly. The
|
||||||
|
[reflection example](https://github.com/dvidelabs/flatcc/blob/master/samples/reflection/bfbs2json.c)
|
||||||
|
mentioned above uses this approach.
|
||||||
|
|
||||||
|
|
||||||
|
## Checking for Present Members
|
||||||
|
|
||||||
|
Not all languages support testing if a field is present, but in C we can
|
||||||
|
elaborate the reader section of the tutorial with tests for this. Recall
|
||||||
|
that `mana` was set to the default value `150` and therefore shouldn't
|
||||||
|
be present.
|
||||||
|
|
||||||
|
<div class="language-c">
|
||||||
|
~~~{.c}
|
||||||
|
int hp_present = ns(Monster_hp_is_present(monster)); // 1
|
||||||
|
int mana_present = ns(Monster_mana_is_present(monster)); // 0
|
||||||
|
~~~
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Alternative ways to add a Union
|
||||||
|
|
||||||
|
In the tutorial we used a single call to add a union. Here we show
|
||||||
|
different ways to accomplish the same thing. The last form is rarely
|
||||||
|
used, but is the low-level way to do it. It can be used to group small
|
||||||
|
values together in the table by adding type and data at different
|
||||||
|
points in time.
|
||||||
|
|
||||||
|
<div class="language-c">
|
||||||
|
~~~{.c}
|
||||||
|
ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe));
|
||||||
|
ns(Monster_equipped_add(B, equipped));
|
||||||
|
// or alternatively
|
||||||
|
ns(Monster_equipped_Weapon_add(B, axe);
|
||||||
|
// or alternatively
|
||||||
|
ns(Monster_equipped_type_add(B, ns(Equipment_Weapon));
|
||||||
|
ns(Monster_equipped_add_member(B, axe));
|
||||||
|
~~~
|
||||||
|
</div>
|
||||||
|
|
||||||
## Why not integrate with the `flatc` tool?
|
## Why not integrate with the `flatc` tool?
|
||||||
|
|
||||||
[It was considered how the C code generator could be integrated into the
|
[It was considered how the C code generator could be integrated into the
|
||||||
@@ -55,3 +217,5 @@ a complicated intermediate representation would have to be invented.
|
|||||||
Neither of these alternatives are very attractive, and it isn't a big
|
Neither of these alternatives are very attractive, and it isn't a big
|
||||||
deal to use the `flatcc` tool instead of `flatc` given that the
|
deal to use the `flatcc` tool instead of `flatc` given that the
|
||||||
FlatBuffers C runtime library needs to be made available regardless.
|
FlatBuffers C runtime library needs to be made available regardless.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ sections provide a more in-depth usage guide.
|
|||||||
in your own programs.
|
in your own programs.
|
||||||
- How to [use the generated Go code](@ref flatbuffers_guide_use_go) in your
|
- How to [use the generated Go code](@ref flatbuffers_guide_use_go) in your
|
||||||
own programs.
|
own programs.
|
||||||
- How to [use the generated C code](@ref flatbuffers_guide_use_c) in your
|
- How to [use FlatBuffers in C with `flatcc`](@ref flatbuffers_guide_use_c) in your
|
||||||
own programs.
|
own programs.
|
||||||
- [Support matrix](@ref flatbuffers_support) for platforms/languages/features.
|
- [Support matrix](@ref flatbuffers_support) for platforms/languages/features.
|
||||||
- Some [benchmarks](@ref flatbuffers_benchmarks) showing the advantage of
|
- Some [benchmarks](@ref flatbuffers_benchmarks) showing the advantage of
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Buffer verifier | Yes | No | No | No | No | No
|
|||||||
Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | ?
|
Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | ?
|
||||||
Testing: fuzz | Yes | No | No | Yes | Yes | No | No | ? | ?
|
Testing: fuzz | Yes | No | No | Yes | Yes | No | No | ? | ?
|
||||||
Performance: | Superb | Great | Great | Great | Ok | ? |Superb| ? | ?
|
Performance: | Superb | Great | Great | Great | Ok | ? |Superb| ? | ?
|
||||||
Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | No | ? | ?
|
Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | ? | ? | ?
|
||||||
Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | ? | ?
|
Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | ? | ?
|
||||||
Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | ? | ?
|
Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | ? | ?
|
||||||
Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ?
|
Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ?
|
||||||
|
|||||||
@@ -234,7 +234,6 @@ FlatBuffer compiler.
|
|||||||
Once `flatc` is built successfully, compile the schema for your language:
|
Once `flatc` is built successfully, compile the schema for your language:
|
||||||
|
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
|
|
||||||
*Note: If you're working in C, you need to use the separate project [FlatCC](https://github.com/dvidelabs/flatcc) which contains a schema compiler and runtime library in C for C.*
|
*Note: If you're working in C, you need to use the separate project [FlatCC](https://github.com/dvidelabs/flatcc) which contains a schema compiler and runtime library in C for C.*
|
||||||
<br>
|
<br>
|
||||||
See [flatcc build instructions](https://github.com/dvidelabs/flatcc#building).
|
See [flatcc build instructions](https://github.com/dvidelabs/flatcc#building).
|
||||||
@@ -391,14 +390,11 @@ The first step is to import/include the library, generated files, etc.
|
|||||||
#include "monster_builder.h" // Generated by `flatcc`.
|
#include "monster_builder.h" // Generated by `flatcc`.
|
||||||
|
|
||||||
// Convenient namespace macro to manage long namespace prefix.
|
// Convenient namespace macro to manage long namespace prefix.
|
||||||
|
#undef ns
|
||||||
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
|
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
|
||||||
// Convenient common namespace macro.
|
|
||||||
#define nsc(x) FLATBUFFERS_WRAP_NAMESPACE(flatbuffers, x)
|
|
||||||
// A helper to simplify creating vectors from C-arrays.
|
// A helper to simplify creating vectors from C-arrays.
|
||||||
#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
|
#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
|
||||||
|
|
||||||
// The ns macro makes it possible to write `ns(Monster_create(...))`
|
|
||||||
// instead of `MyGame_Sample_Monster_create(...)`.
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -577,10 +573,10 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
ns(Weapon_ref_t) weapon_one_name = nsc(string_create_str(B, "Sword"));
|
ns(Weapon_ref_t) weapon_one_name = flatbuffers_string_create_str(B, "Sword");
|
||||||
uint16_t weapon_one_damage = 3;
|
uint16_t weapon_one_damage = 3;
|
||||||
|
|
||||||
ns(Weapon_ref_t) weapon_two_name = nsc(string_create_str(B, "Axe"));
|
ns(Weapon_ref_t) weapon_two_name = flatbuffers_string_create_str(B, "Axe");
|
||||||
uint16_t weapon_two_damage = 5;
|
uint16_t weapon_two_damage = 5;
|
||||||
|
|
||||||
ns(Weapon_ref_t) sword = ns(Weapon_create(B, weapon_one_name, weapon_one_damage));
|
ns(Weapon_ref_t) sword = ns(Weapon_create(B, weapon_one_name, weapon_one_damage));
|
||||||
@@ -693,14 +689,14 @@ traversal. This is generally easy to do on any tree structures.
|
|||||||
~~~{.c}
|
~~~{.c}
|
||||||
// Serialize a name for our monster, called "Orc".
|
// Serialize a name for our monster, called "Orc".
|
||||||
// The _str suffix indicates the source is an ascii-z string.
|
// The _str suffix indicates the source is an ascii-z string.
|
||||||
nsc(string_ref_t) name = nsc(string_create_str(B, "Orc"));
|
flatbuffers_string_ref_t name = flatbuffers_string_create_str(B, "Orc");
|
||||||
|
|
||||||
// Create a `vector` representing the inventory of the Orc. Each number
|
// Create a `vector` representing the inventory of the Orc. Each number
|
||||||
// could correspond to an item that can be claimed after he is slain.
|
// could correspond to an item that can be claimed after he is slain.
|
||||||
uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
nsc(uint8_vec_ref_t) inventory;
|
flatbuffers_uint8_vec_ref_t inventory;
|
||||||
// `c_vec_len` is the convenience macro we defined earlier.
|
// `c_vec_len` is the convenience macro we defined earlier.
|
||||||
inventory = nsc(uint8_vec_create(B, treasure, c_vec_len(treasure)));
|
inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -718,13 +714,6 @@ and `Axe`). These are both FlatBuffer `table`s, whose offsets we now store in
|
|||||||
memory. Therefore we can create a FlatBuffer `vector` to contain these
|
memory. Therefore we can create a FlatBuffer `vector` to contain these
|
||||||
offsets.
|
offsets.
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: If you're using C, there is also an often shorter top-down
|
|
||||||
approach that avoids storing temporary references because the runtime
|
|
||||||
has an internal stack. The top-down version is shown at the end of build
|
|
||||||
section.*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="language-cpp">
|
<div class="language-cpp">
|
||||||
~~~{.cpp}
|
~~~{.cpp}
|
||||||
// Place the weapons into a `std::vector`, then convert that into a FlatBuffer `vector`.
|
// Place the weapons into a `std::vector`, then convert that into a FlatBuffer `vector`.
|
||||||
@@ -794,10 +783,7 @@ section.*
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
// Here we use a top-down approach locally to build a Weapons vector
|
// We use the internal builder stack to implement a dynamic vector.
|
||||||
// in-place instead of creating a temporary external vector to use
|
|
||||||
// as argument like we did with the `inventory` earlier on, but the
|
|
||||||
// overall approach is still bottom-up.
|
|
||||||
ns(Weapon_vec_start(B));
|
ns(Weapon_vec_start(B));
|
||||||
ns(Weapon_vec_push(B, sword));
|
ns(Weapon_vec_push(B, sword));
|
||||||
ns(Weapon_vec_push(B, axe));
|
ns(Weapon_vec_push(B, axe));
|
||||||
@@ -970,31 +956,23 @@ can serialize the monster itself:
|
|||||||
uint16_t hp = 300;
|
uint16_t hp = 300;
|
||||||
uint16_t mana = 150;
|
uint16_t mana = 150;
|
||||||
|
|
||||||
// Create the equipment union. In the C++ language API this is given
|
// Define an equipment union. `create` calls in C has a single
|
||||||
// as two arguments to the create call, or as two separate add
|
// argument for unions where C++ has both a type and a data argument.
|
||||||
// operations for the type and the table reference. In C we create
|
|
||||||
// a single union value that carries both the type and reference.
|
|
||||||
ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe));
|
ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe));
|
||||||
|
|
||||||
ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red),
|
ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red),
|
||||||
weapons, equipped));
|
weapons, equipped));
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: in C we use `create_as_root` instead of the also valid `create` call
|
|
||||||
because it simplfies constructing the root object.*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="language-cpp">
|
|
||||||
<br>
|
|
||||||
*Note: Since we are passing `150` as the `mana` field, which happens to be the
|
*Note: Since we are passing `150` as the `mana` field, which happens to be the
|
||||||
default value, the field will not actually be written to the buffer, since the
|
default value, the field will not actually be written to the buffer, since the
|
||||||
default value will be returned on query anyway. This is a nice space savings,
|
default value will be returned on query anyway. This is a nice space savings,
|
||||||
especially if default values are common in your data. It also means that you do
|
especially if default values are common in your data. It also means that you do
|
||||||
not need to be worried of adding a lot of fields that are only used in a small
|
not need to be worried of adding a lot of fields that are only used in a small
|
||||||
number of instances, as it will not bloat the buffer if unused.*
|
number of instances, as it will not bloat the buffer if unused.*
|
||||||
<br><br>
|
|
||||||
|
<div class="language-cpp">
|
||||||
|
<br>
|
||||||
If you do not wish to set every field in a `table`, it may be more convenient to
|
If you do not wish to set every field in a `table`, it may be more convenient to
|
||||||
manually set each field of your monster, instead of calling `CreateMonster()`.
|
manually set each field of your monster, instead of calling `CreateMonster()`.
|
||||||
The following snippet is functionally equivalent to the above code, but provides
|
The following snippet is functionally equivalent to the above code, but provides
|
||||||
@@ -1016,14 +994,6 @@ a bit more flexibility.
|
|||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
<br>
|
|
||||||
*Note: Since we are passing `150` as the `mana` field, which happens to be the
|
|
||||||
default value, the field will not actually be written to the buffer, since the
|
|
||||||
default value will be returned on query anyway. This is a nice space savings,
|
|
||||||
especially if default values are common in your data. It also means that you do
|
|
||||||
not need to be worried of adding a lot of fields that are only used in a small
|
|
||||||
number of instances, as it will not bloat the buffer if unused.*
|
|
||||||
<br><br>
|
|
||||||
If you do not wish to set every field in a `table`, it may be more convenient to
|
If you do not wish to set every field in a `table`, it may be more convenient to
|
||||||
manually set each field of your monster, instead of calling `create_monster_as_root()`.
|
manually set each field of your monster, instead of calling `create_monster_as_root()`.
|
||||||
The following snippet is functionally equivalent to the above code, but provides
|
The following snippet is functionally equivalent to the above code, but provides
|
||||||
@@ -1058,11 +1028,6 @@ Second, is the `union`'s data.
|
|||||||
In our example, the last two things we added to our `Monster` were the
|
In our example, the last two things we added to our `Monster` were the
|
||||||
`Equipped Type` and the `Equipped` union itself.
|
`Equipped Type` and the `Equipped` union itself.
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: In C, several different helpers make these two fields appear as
|
|
||||||
one field, but they can be added separately.*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Here is a repetition these lines, to help highlight them more clearly:
|
Here is a repetition these lines, to help highlight them more clearly:
|
||||||
|
|
||||||
<div class="language-cpp">
|
<div class="language-cpp">
|
||||||
@@ -1110,62 +1075,8 @@ Here is a repetition these lines, to help highlight them more clearly:
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe));
|
// Add union type and data simultanously.
|
||||||
ns(Monster_equipped_add(B, equipped));
|
ns(Monster_equipped_Weapon_add(B, axe));
|
||||||
// or alternatively
|
|
||||||
ns(Monster_equipped_Weapon_add(B, axe);
|
|
||||||
// or alternatively
|
|
||||||
ns(Monster_equipped_type_add(B, ns(Equipment_Weapon));
|
|
||||||
ns(Monster_equipped_add_member(B, axe));
|
|
||||||
~~~
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
Here is an alternative top-down approach unique to the C builder
|
|
||||||
library.
|
|
||||||
<br>
|
|
||||||
~~~{.c}
|
|
||||||
uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
|
||||||
size_t treasure_count = c_vec_len(treasure);
|
|
||||||
ns(Weapon_ref_t) axe;
|
|
||||||
|
|
||||||
// NOTE: if we use end_as_root, we MUST also start as root.
|
|
||||||
ns(Monster_start_as_root(B));
|
|
||||||
ns(Monster_pos_create(B, 1.0f, 2.0f, 3.0f));
|
|
||||||
ns(Monster_hp_add(B, 300));
|
|
||||||
ns(Monster_mana_add(B, 150));
|
|
||||||
// We use create_str instead of add because we have no existing string reference.
|
|
||||||
ns(Monster_name_create_str(B, "Orc"));
|
|
||||||
// Again we use create because we no existing vector object, only a C-array.
|
|
||||||
ns(Monster_inventory_create(B, treasure, treasure_count));
|
|
||||||
ns(Monster_color_add(B, ns(Color_Red)));
|
|
||||||
if (1) {
|
|
||||||
ns(Monster_weapons_start(B));
|
|
||||||
ns(Monster_weapons_push_create(B, nsc(string_create_str(B, "Sword")), 3));
|
|
||||||
// We reuse the axe object later. Note that we dereference a pointer
|
|
||||||
// because push always returns a short-term pointer to the stored element.
|
|
||||||
// We could also have created the axe object first and simply pushed it.
|
|
||||||
axe = *ns(Monster_weapons_push_create(B, nsc(string_create_str(B, "Axe")), 5));
|
|
||||||
ns(Monster_weapons_end(B));
|
|
||||||
} else {
|
|
||||||
// We can have more control with the table elements added to a vector:
|
|
||||||
//
|
|
||||||
ns(Monster_weapons_start(B));
|
|
||||||
ns(Monster_weapons_push_start(B));
|
|
||||||
ns(Weapon_name_create_str(B, "Sword"));
|
|
||||||
ns(Weapon_damage_add(B, 3));
|
|
||||||
ns(Monster_weapons_push_end(B));
|
|
||||||
ns(Monster_weapons_push_start(B));
|
|
||||||
ns(Monster_weapons_push_start(B));
|
|
||||||
ns(Weapon_name_create_str(B, "Axe"));
|
|
||||||
ns(Weapon_damage_add(B, 5));
|
|
||||||
axe = *ns(Monster_weapons_push_end(B));
|
|
||||||
ns(Monster_weapons_end(B));
|
|
||||||
}
|
|
||||||
// Unions can get their type by using a type-specific add/create/start method.
|
|
||||||
ns(Monster_equipped_Weapon_add(B, axe));
|
|
||||||
|
|
||||||
ns(Monster_end_as_root(B));
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1173,12 +1084,6 @@ After you have created your buffer, you will have the offset to the root of the
|
|||||||
data in the `orc` variable, so you can finish the buffer by calling the
|
data in the `orc` variable, so you can finish the buffer by calling the
|
||||||
appropriate `finish` method.
|
appropriate `finish` method.
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: C does not have a `finish` call, and it is not needed when we use
|
|
||||||
`create_as_root` or `start/end_as_root`. For the sake of modularity, it
|
|
||||||
is sometimes useful to create an object without knowing if it will be a
|
|
||||||
root. We show this below, but do NOT mix it with the `_as_root` calls.*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="language-cpp">
|
<div class="language-cpp">
|
||||||
~~~{.cpp}
|
~~~{.cpp}
|
||||||
@@ -1229,10 +1134,7 @@ root. We show this below, but do NOT mix it with the `_as_root` calls.*
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
// Alternative approach separating object creation from being root object.
|
// Because we used `Monster_create_as_root`, we do not need a `finish` call in C`.
|
||||||
ns(Monster_ref_t) orc = ns(Monster_create(B, ...));
|
|
||||||
// `flatcc_` calls should be isolated to top-level driver logic.
|
|
||||||
flatcc_builder_buffer_create(orc);
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1317,12 +1219,6 @@ deserialize a FlatBuffer.
|
|||||||
This section requires the same import/include, namespace, etc. requirements as
|
This section requires the same import/include, namespace, etc. requirements as
|
||||||
before:
|
before:
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: In C there is a separate include file for the reader which is automatically
|
|
||||||
included by the generated builder header. A standalone reader only depends on header
|
|
||||||
files while the builder must link with a small runtime library.*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="language-cpp">
|
<div class="language-cpp">
|
||||||
~~~{.cpp}
|
~~~{.cpp}
|
||||||
#include "monster_generate.h" // This was generated by `flatc`.
|
#include "monster_generate.h" // This was generated by `flatc`.
|
||||||
@@ -1401,10 +1297,11 @@ files while the builder must link with a small runtime library.*
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
|
// Only needed if we don't have `#include "monster_builder.h"`.
|
||||||
#include "monster_reader.h"
|
#include "monster_reader.h"
|
||||||
|
|
||||||
|
#undef ns
|
||||||
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
|
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
|
||||||
#define nsc(x) FLATBUFFERS_WRAP_NAMESPACE(flatbuffers, x)
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1565,29 +1462,14 @@ accessors for all non-`deprecated` fields. For example:
|
|||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
uint16_t hp = ns(Monster_hp(monster));
|
uint16_t hp = ns(Monster_hp(monster));
|
||||||
// Since 150 is the default, we are reading a value that wasn't stored.
|
|
||||||
uint16_t mana = ns(Monster_mana(monster));
|
uint16_t mana = ns(Monster_mana(monster));
|
||||||
// This is just a const char *, but it also supports a fast length operation.
|
flatbuffers_string_t name = ns(Monster_name(monster));
|
||||||
nsc(string_t) name = ns(Monster_name(monster));
|
|
||||||
size_t name_len = nsc(string_len(name));
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: In C we can check if a field is present. For example `mana`
|
|
||||||
should not be present because it was set with a default value or not at
|
|
||||||
all, but `hp` should be present.*
|
|
||||||
|
|
||||||
~~~{.c}
|
|
||||||
int hp_present = ns(Monster_hp_is_present(monster)); // 1
|
|
||||||
int mana_present = ns(Monster_mana_is_present(monster)); // 0
|
|
||||||
~~~
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
These should hold `300`, `150`, and `"Orc"` respectively.
|
These should hold `300`, `150`, and `"Orc"` respectively.
|
||||||
|
|
||||||
*Note: We never stored a value in `mana`, so we got the default value of `150`.*
|
*Note: The default value `150` wasn't stored in `mana`, but we are still able to retrieve it.*
|
||||||
|
|
||||||
To access sub-objects, in the case of our `pos`, which is a `Vec3`:
|
To access sub-objects, in the case of our `pos`, which is a `Vec3`:
|
||||||
|
|
||||||
@@ -1659,13 +1541,6 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`:
|
|||||||
float x = ns(Vec3_x(pos));
|
float x = ns(Vec3_x(pos));
|
||||||
float y = ns(Vec3_y(pos));
|
float y = ns(Vec3_y(pos));
|
||||||
float z = ns(Vec3_z(pos));
|
float z = ns(Vec3_z(pos));
|
||||||
|
|
||||||
// or alternatively
|
|
||||||
ns(Vec3_t) pos_vec;
|
|
||||||
// `pe` indicates endian conversion from protocol to native.
|
|
||||||
ns(Vec3_copy_from_pe(&pos_vec, pos));
|
|
||||||
x = pos_vec.x;
|
|
||||||
// ...
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1722,15 +1597,10 @@ FlatBuffers `vector`.
|
|||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.c}
|
~~~{.c}
|
||||||
// This is a const uint8_t *, but it shouldn't be accessed directly
|
// If `inv` hasn't been set, it will be null. It is valid get
|
||||||
// to ensure proper endian conversion. Incidentally the uint8 (ubyte)
|
// the length of null which will be 0, useful for iteration.
|
||||||
// is not sensitive to endianness, so we *could* have accessed it directly.
|
flatbuffers_uint8_vec_t inv = ns(Monster_inventory(monster));
|
||||||
// The compiler likely optimizes this so that it doesn't matter.
|
size_t inv_len = flatbuffers_uint8_vec_len(inv);
|
||||||
nsc(uint8_vec_t) inv = ns(Monster_inventory(monster));
|
|
||||||
size_t inv_len = nsc(uint8_vec_len(inv));
|
|
||||||
|
|
||||||
// If `inv` was not set, it will be null, but the length is still
|
|
||||||
// valid to read and will then be zero.
|
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1795,7 +1665,7 @@ except your need to handle the result as a FlatBuffer `table`:
|
|||||||
~~~{.c}
|
~~~{.c}
|
||||||
ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster));
|
ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster));
|
||||||
size_t weapons_len = ns(Weapon_vec_len(weapons));
|
size_t weapons_len = ns(Weapon_vec_len(weapons));
|
||||||
// We don't have to use `nsc(string_t)` as type if we don't need fast length access.
|
// We can use `const char *` instead of `flatbuffers_string_t`.
|
||||||
const char *second_weapon_name = ns(Weapon_name(ns(Weapon_vec_at(weapons, 1))));
|
const char *second_weapon_name = ns(Weapon_name(ns(Weapon_vec_at(weapons, 1))));
|
||||||
uint16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1))));
|
uint16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1))));
|
||||||
~~~
|
~~~
|
||||||
@@ -1917,11 +1787,6 @@ We can access the type to dynamically cast the data as needed (since the
|
|||||||
|
|
||||||
## Mutating FlatBuffers
|
## Mutating FlatBuffers
|
||||||
|
|
||||||
<div class="language-c">
|
|
||||||
*Note: This section does not fully apply to C which has no generated mutation
|
|
||||||
interface (except for sorting vectors in-place which is an advanced topic).*
|
|
||||||
</div>
|
|
||||||
|
|
||||||
As you saw above, typically once you have created a FlatBuffer, it is read-only
|
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
|
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
|
FlatBuffer, and you'd like to modify something about it before sending it on to
|
||||||
@@ -1984,8 +1849,9 @@ mutators like so:
|
|||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
<div class="language-c">
|
<div class="language-c">
|
||||||
~~~{.php}
|
~~~{.c}
|
||||||
<API for in-place mutating FlatBuffers will not be supported in C.>
|
<API for in-place mutating FlatBuffers will not be supported in C
|
||||||
|
(except in-place vector sorting is possible).>
|
||||||
~~~
|
~~~
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user