Documentation for C bindings

This commit is contained in:
Mikkel Fahnøe Jørgensen
2016-03-26 00:28:31 +01:00
parent a649cb7db5
commit 47d4b46950
6 changed files with 454 additions and 20 deletions

View File

@@ -23,12 +23,13 @@ Please select your desired language for our quest:
\htmlonly
<form>
<input type="radio" name="language" value="cpp" checked="checked">C++</input>
<input type="radio" name="language" value="java">Java</input>
<input type="radio" name="language" value="csharp">C#</input>
<input type="radio" name="language" value="c">C</input>
<input type="radio" name="language" value="go">Go</input>
<input type="radio" name="language" value="python">Python</input>
<input type="radio" name="language" value="java">Java</input>
<input type="radio" name="language" value="javascript">JavaScript</input>
<input type="radio" name="language" value="php">PHP</input>
<input type="radio" name="language" value="python">Python</input>
</form>
\endhtmlonly
@@ -98,6 +99,10 @@ Samples demonstating the concepts in this example are located in the source code
package, under the `samples` directory. You can browse the samples on GitHub
[here](https://github.com/google/flatbuffers/tree/master/samples).
<div class="language-c">
*Note: The above does not apply to C, instead [look here](https://github.com/dvidelabs/flatcc/tree/master/samples).*
</div>
For your chosen language, please cross-reference with:
<div class="language-cpp">
@@ -121,6 +126,9 @@ For your chosen language, please cross-reference with:
<div class="language-php">
[SampleBinary.php](https://github.com/google/flatbuffers/blob/master/samples/SampleBinary.php)
</div>
<div class="language-c">
[monster.c](https://github.com/dvidelabs/flatcc/blob/master/samples/monster/monster.c)
</div>
## Writing the Monsters' FlatBuffer Schema
@@ -225,6 +233,16 @@ FlatBuffer compiler.
Once `flatc` is built successfully, compile the schema for your language:
<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.*
<br>
See [flatcc build instructions](https://github.com/dvidelabs/flatcc#building).
<br>
Please be aware of the difference between `flatc` and `flatcc` tools.
<br>
</div>
<div class="language-cpp">
~~~{.sh}
cd flatbuffers/sample
@@ -267,8 +285,17 @@ Once `flatc` is built successfully, compile the schema for your language:
./../flatc --php samples/monster.fbs
~~~
</div>
<div class="language-c">
~~~{.sh}
cd flatcc
mkdir -p build/tmp/samples/monster
bin/flatcc -a -o build/tmp/samples/monster samples/monster/monster.fbs
# or just
flatcc/samples/monster/build.sh
~~~
</div>
For a more complete guide to using the `flatc` compiler, pleaes read the
For a more complete guide to using the `flatc` compiler, please read the
[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
section of the Programmer's Guide.
@@ -359,6 +386,21 @@ The first step is to import/include the library, generated files, etc.
}
~~~
</div>
<div class="language-c">
~~~{.c}
#include "monster_builder.h" // Generated by `flatcc`.
// Convenient namespace macro to manage long namespace prefix.
#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.
#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>
Now we are ready to start building some buffers. In order to start, we need
to create an instance of the `FlatBufferBuilder`, which will contain the buffer
@@ -413,6 +455,14 @@ as it grows:
$builder = new Google\FlatBuffers\FlatbufferBuilder(0);
~~~
</div>
<div class="language-c">
~~~{.c}
flatcc_builder_t builder, *B;
B = &builder;
// Initialize the builder object.
flatcc_builder_init(B);
~~~
</div>
After creating the `builder`, we can start serializing our data. Before we make
our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
@@ -525,6 +575,18 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`.
$weapons = \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps);
~~~
</div>
<div class="language-c">
~~~{.c}
ns(Weapon_ref_t) weapon_one_name = nsc(string_create_str(B, "Sword"));
uint16_t weapon_one_damage = 3;
ns(Weapon_ref_t) weapon_two_name = nsc(string_create_str(B, "Axe"));
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) axe = ns(Weapon_create(B, weapon_two_name, weapon_two_damage));
~~~
</div>
Now let's create our monster, the `orc`. For this `orc`, lets make him
`red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him
@@ -627,6 +689,20 @@ traversal. This is generally easy to do on any tree structures.
$inv = \MyGame\Sample\Monster::CreateInventoryVector($builder, $treasure);
~~~
</div>
<div class="language-c">
~~~{.c}
// Serialize a name for our monster, called "Orc".
// The _str suffix indicates the source is an ascii-z string.
nsc(string_ref_t) name = nsc(string_create_str(B, "Orc"));
// Create a `vector` representing the inventory of the Orc. Each number
// 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};
nsc(uint8_vec_ref_t) inventory;
// `c_vec_len` is the convenience macro we defined earlier.
inventory = nsc(uint8_vec_create(B, treasure, c_vec_len(treasure)));
~~~
</div>
We serialized two built-in data types (`string` and `vector`) and captured
their return values. These values are offsets into the serialized data,
@@ -642,6 +718,13 @@ 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
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">
~~~{.cpp}
// Place the weapons into a `std::vector`, then convert that into a FlatBuffer `vector`.
@@ -709,8 +792,21 @@ offsets.
$weapons = \MyGame\Sample\Monster::CreateWeaponsVector($builder, $weaps);
~~~
</div>
<div class="language-c">
~~~{.c}
// Here we use a top-down approach locally to build a Weapons 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_push(B, sword));
ns(Weapon_vec_push(B, axe));
ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B));
~~~
</div>
To create a `struct`, use the `Vec3` class/struct that was generated by `flatc`:
To create a `struct`, use the `Vec3` class/struct that was generated by
the schema compiler:
<div class="language-cpp">
~~~{.cpp}
@@ -754,6 +850,12 @@ To create a `struct`, use the `Vec3` class/struct that was generated by `flatc`:
$pos = \MyGame\Sample\Vec3::CreateVec3($builder, 1.0, 2.0, 3.0);
~~~
</div>
<div class="language-c">
~~~{.c}
// Create a `Vec3`, representing the Orc's position in 3-D space.
ns(Vec3_t) pos = { 1.0f, 2.0f, 3.0f };
~~~
</div>
We have now serialized the non-scalar components of the orc, so we
can serialize the monster itself:
@@ -862,10 +964,31 @@ can serialize the monster itself:
$orc = \MyGame\Sample\Monster::EndMonster($builder);
~~~
</div>
<div class="language-c">
~~~{.c}
// Set his hit points to 300 and his mana to 150.
uint16_t hp = 300;
uint16_t mana = 150;
// Create the equipment union. In the C++ language API this is given
// as two arguments to the create call, or as two separate add
// 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(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red),
weapons, equipped));
~~~
</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 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 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
@@ -892,6 +1015,39 @@ a bit more flexibility.
auto orc = monster_builder.Finish();
~~~
</div>
<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
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
a bit more flexibility.
<br>
~~~{.c}
// It is important to pair `start_as_root` with `end_as_root`.
ns(Monster_start_as_root(B));
ns(Monster_pos_create(B, 1.0f, 2.0f, 3.0f));
// or alternatively
//ns(Monster_pos_add(&pos);
ns(Monster_hp_add(B, hp));
// Notice that `Monser_name_add` adds a string reference unlike the
// add_str and add_strn variants.
ns(Monster_name_add(B, name));
ns(Monster_inventory_add(B, inventory));
ns(Monster_color_add(B, ns(Color_Red)));
ns(Monster_weapons_add(B, weapons));
ns(Monster_equipped_add(B, equipped));
// Complete the monster object and make it the buffer root object.
ns(Monster_end_as_root(B));
~~~
</div>
Before finishing the serialization, let's take a quick look at FlatBuffer
`union Equipped`. There are two parts to each FlatBuffer `union`. The first, is
@@ -902,6 +1058,11 @@ Second, is the `union`'s data.
In our example, the last two things we added to our `Monster` were the
`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:
<div class="language-cpp">
@@ -947,11 +1108,78 @@ Here is a repetition these lines, to help highlight them more clearly:
\MyGame\Sample\Monster::AddEquipped($builder, $axe); // Union data
~~~
</div>
<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>
<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>
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
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">
~~~{.cpp}
// Call `Finish()` to instruct the builder that this monster is complete.
@@ -999,6 +1227,14 @@ appropriate `finish` method.
// $builder, $orc);`.
~~~
</div>
<div class="language-c">
~~~{.c}
// Alternative approach separating object creation from being root object.
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>
The buffer is now ready to be stored somewhere, sent over the network, be
compressed, or whatever you'd like to do with it. You can access the buffer
@@ -1048,6 +1284,29 @@ like so:
$buf = $builder->dataBuffer(); // Of type `Google\FlatBuffers\ByteBuffer`
~~~
</div>
<div class="language-c">
~~~{.c}
uint8_t *buf;
size_t size;
// Allocate and extract a readable buffer from internal builder heap.
// The returned buffer must be deallocated using `free`.
// NOTE: Finalizing the buffer does NOT change the builder, it
// just creates a snapshot of the builder content.
buf = flatcc_builder_finalize_buffer(&builder, &size);
// use buf
free(buf);
// Optionally reset builder to reuse builder without deallocating
// internal stack and heap.
flatcc_builder_reset(B);
// build next buffer.
// ...
// Cleanup.
flatcc_builder_clear(B);
~~~
</div>
#### Reading Orc FlatBuffers
@@ -1055,9 +1314,15 @@ Now that we have successfully created an `Orc` FlatBuffer, the monster data can
be saved, sent over a network, etc. Let's now adventure into the inverse, and
deserialize a FlatBuffer.
This seciton requires the same import/include, namespace, etc. requirements as
This section requires the same import/include, namespace, etc. requirements as
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">
~~~{.cpp}
#include "monster_generate.h" // This was generated by `flatc`.
@@ -1134,6 +1399,14 @@ before:
}
~~~
</div>
<div class="language-c">
~~~{.c}
#include "monster_reader.h"
#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
#define nsc(x) FLATBUFFERS_WRAP_NAMESPACE(flatbuffers, x)
~~~
</div>
Then, assuming you have a variable containing to the bytes of data from disk,
network, etc., you can create a monster from this data:
@@ -1224,8 +1497,18 @@ network, etc., you can create a monster from this data:
$monster = \MyGame\Sample\Monster::GetRootAsMonster($buf);
~~~
</div>
<div class="language-c">
~~~{.c}
// Note that we use the `table_t` suffix when reading a table object
// as opposed to the `ref_t` suffix used during the construction of
// the buffer.
ns(Monster_table_t) monster = ns(Monster_as_root(buffer));
If you look in the generated files from `flatc`, you will see it generated
// Note: root object pointers are NOT the same as the `buffer` pointer.
~~~
</div>
If you look in the generated files from the schema compiler, you will see it generated
accessors for all non-`deprecated` fields. For example:
<div class="language-cpp">
@@ -1279,10 +1562,32 @@ accessors for all non-`deprecated` fields. For example:
$name = monster->getName();
~~~
</div>
<div class="language-c">
~~~{.c}
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));
// This is just a const char *, but it also supports a fast length operation.
nsc(string_t) name = ns(Monster_name(monster));
size_t name_len = nsc(string_len(name));
~~~
</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.
*Note: We never stored a value in `mp`, so we got the default value of `150`.*
*Note: We never stored a value in `mana`, so we got the default value of `150`.*
To access sub-objects, in the case of our `pos`, which is a `Vec3`:
@@ -1348,10 +1653,25 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`:
$z = $pos->getZ();
~~~
</div>
<div class="language-c">
~~~{.c}
ns(Vec3_struct_t) pos = ns(Monster_pos(monster));
float x = ns(Vec3_x(pos));
float y = ns(Vec3_y(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>
`x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively.
*Note: Had we not set `pos` during serialization, it would be `NULL`-value.*
*Note: Had we not set `pos` during serialization, it would be a `NULL`-value.*
Similarly, we can access elements of the inventory `vector` by indexing it. You
can also iterate over the length of the array/vector representing the
@@ -1400,6 +1720,19 @@ FlatBuffers `vector`.
$third_item = $monster->getInventory(2);
~~~
</div>
<div class="language-c">
~~~{.c}
// This is a const uint8_t *, but it shouldn't be accessed directly
// to ensure proper endian conversion. Incidentally the uint8 (ubyte)
// is not sensitive to endianness, so we *could* have accessed it directly.
// The compiler likely optimizes this so that it doesn't matter.
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>
For `vector`s of `table`s, you can access the elements like any other vector,
except your need to handle the result as a FlatBuffer `table`:
@@ -1458,6 +1791,15 @@ except your need to handle the result as a FlatBuffer `table`:
$second_weapon_damage = $monster->getWeapons(1)->getDamage();
~~~
</div>
<div class="language-c">
~~~{.c}
ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster));
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.
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))));
~~~
</div>
Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created
the `union`, we need to get both parts of the `union`: the type and the data.
@@ -1560,9 +1902,26 @@ We can access the type to dynamically cast the data as needed (since the
}
~~~
</div>
<div class="language-c">
~~~{.c}
// Access union type field.
if (ns(Monster_equipped_type(monster)) == ns(Equipment_Weapon)) {
// Cast to appropriate type:
// C allows for silent void pointer assignment, so we need no explicit cast.
ns(Weapon_table_t) weapon = ns(Monster_equipped(monster));
const char *weapon_name = ns(Weapon_name(weapon)); // "Axe"
uint16_t weapon_damage = ns(Weapon_damage(weapon)); // 5
}
~~~
</div>
## 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
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
@@ -1624,6 +1983,11 @@ mutators like so:
<API for mutating FlatBuffers is not yet supported in PHP.>
~~~
</div>
<div class="language-c">
~~~{.php}
<API for in-place mutating FlatBuffers will not be supported in C.>
~~~
</div>
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
@@ -1687,6 +2051,14 @@ FlatBuffer binary representation of the contents from our `.json` file.
[Use in C++](@ref flatbuffers_guide_use_cpp) section of the Programmer's
Guide for more information.*
</div>
<div class="language-c">
*Note: If you're working in C, the `flatcc --json` (not `flatc`)
compiler will generate schema specific high performance json parsers and
printers that you can compile and use at runtime. The `flatc` compiler (not
`flatcc`) on the other hand, is still useful for general offline json to
flatbuffer conversion from a given schema. There are no current plans
for `flatcc` to support this.*
</div>
## Advanced Features for Each Language
@@ -1716,5 +2088,8 @@ For your chosen language, see:
<div class="language-php">
[Use in PHP](@ref flatbuffers_guide_use_php)
</div>
<div class="language-c">
[Use in C](@ref flatbuffers_guide_use_c)
</div>
<br>