225 lines
8.1 KiB
Markdown
225 lines
8.1 KiB
Markdown
Use in C {#flatbuffers_guide_use_c}
|
|
==========
|
|
|
|
The C language binding exists in a separate project named [FlatCC](https://github.com/dvidelabs/flatcc).
|
|
|
|
The `flatcc` C schema compiler can generate code offline as well as
|
|
online via a C library. It can also generate buffer verifiers and fast
|
|
JSON parsers, printers.
|
|
|
|
Great care has been taken to ensure compatibily with the main `flatc`
|
|
project.
|
|
|
|
## General Documention
|
|
|
|
- [Tutorial](@ref flatbuffers_guide_tutorial) - select C as language
|
|
when scrolling down
|
|
- [FlatCC Guide](https://github.com/dvidelabs/flatcc#flatcc-flatbuffers-in-c-for-c)
|
|
- [The C Builder Interface](https://github.com/dvidelabs/flatcc/blob/master/doc/builder.md#the-builder-interface)
|
|
- [The Monster Sample in C](https://github.com/dvidelabs/flatcc/blob/master/samples/monster/monster.c)
|
|
- [GitHub](https://github.com/dvidelabs/flatcc)
|
|
|
|
|
|
## Supported Platforms
|
|
|
|
- Ubuntu (clang / gcc, ninja / gnu make)
|
|
- OS-X (clang / gcc, ninja / gnu make)
|
|
- Windows MSVC 2010, 2013, 2015
|
|
|
|
CI builds recent versions of gcc, clang and MSVC on OS-X, Ubuntu, and
|
|
Windows, and occasionally older compiler versions. See main project [Status](https://github.com/dvidelabs/flatcc#status).
|
|
|
|
Other platforms may well work, including Centos, but are not tested
|
|
regularly.
|
|
|
|
The monster sample project was specifically written for C99 in order to
|
|
follow the C++ version and for that reason it will not work with MSVC
|
|
2010.
|
|
|
|
## 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
|
|
|
|
The C-API does support reading binary schema (.bfbs)
|
|
files via code generated from the `reflection.fbs` schema, and an
|
|
[example usage](https://github.com/dvidelabs/flatcc/tree/master/samples/reflection)
|
|
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).
|
|
|
|
|
|
## Mutations and Reflection
|
|
|
|
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).
|
|
|
|
The generated reader interface supports sorting vectors in-place after
|
|
casting them to a mutating type because it is not practical to do so
|
|
while building a buffer. This is covered in the builder documentation.
|
|
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
|
|
these.
|
|
|
|
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
|
|
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
|
|
the best approach. If the namespace is absent, or 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_add_type(B, ns(Equipment_Weapon));
|
|
ns(Monster_equipped_add_member(B, axe));
|
|
~~~
|
|
</div>
|
|
|
|
## Why not integrate with the `flatc` tool?
|
|
|
|
[It was considered how the C code generator could be integrated into the
|
|
`flatc` tool](https://github.com/dvidelabs/flatcc/issues/1), but it
|
|
would either require that the standalone C implementation of the schema
|
|
compiler was dropped, or it would lead to excessive code duplication, or
|
|
a complicated intermediate representation would have to be invented.
|
|
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
|
|
FlatBuffers C runtime library needs to be made available regardless.
|
|
|
|
|