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 effort has been made to ensure compatibily with the main `flatc` project. ## General Documention - [Tutorial](@ref flatbuffers_guide_tutorial) - select C as language when scrolling down - General Use in C (README) - The C Builder Interface (advanced) ## 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:
~~~{.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); } ~~~
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:

~~~{.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)); ~~~
## 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 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.
~~~{.c} int hp_present = ns(Monster_hp_is_present(monster)); // 1 int mana_present = ns(Monster_mana_is_present(monster)); // 0 ~~~
## 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.
~~~{.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)); ~~~
## 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.