aboutsummaryrefslogtreecommitdiff
path: root/flatcc/samples
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2023-07-16 02:03:33 +0200
committerToni Uhlig <matzeton@googlemail.com>2023-07-16 02:03:33 +0200
commitb31e4bc16d1df62b50c6f77a77041f9e7b6c906d (patch)
tree024c74c13d918aa6bde302aab6836fa33607613c /flatcc/samples
parentba6815ef8fb8ae472412b5af2837a7caba2799c2 (diff)
parent5a40295c4cf0af5ea8da9ced04a4ce7d3621a080 (diff)
Merge commit '5a40295c4cf0af5ea8da9ced04a4ce7d3621a080' as 'flatcc'
Diffstat (limited to 'flatcc/samples')
-rw-r--r--flatcc/samples/CMakeLists.txt8
-rw-r--r--flatcc/samples/bugreport/.gitignore1
-rwxr-xr-xflatcc/samples/bugreport/build.sh22
-rw-r--r--flatcc/samples/bugreport/eclectic.fbs11
-rw-r--r--flatcc/samples/bugreport/myissue.c35
-rw-r--r--flatcc/samples/monster/CMakeLists.txt22
-rwxr-xr-xflatcc/samples/monster/build.sh27
-rw-r--r--flatcc/samples/monster/monster.c353
-rw-r--r--flatcc/samples/monster/monster.fbs32
-rw-r--r--flatcc/samples/reflection/CMakeLists.txt31
-rw-r--r--flatcc/samples/reflection/bfbs2json.c314
-rwxr-xr-xflatcc/samples/reflection/build.sh27
12 files changed, 883 insertions, 0 deletions
diff --git a/flatcc/samples/CMakeLists.txt b/flatcc/samples/CMakeLists.txt
new file mode 100644
index 0000000..c05a450
--- /dev/null
+++ b/flatcc/samples/CMakeLists.txt
@@ -0,0 +1,8 @@
+if (FLATCC_NEED_C89_VAR_DECLS)
+ MESSAGE( STATUS "Disabling monster sample: needed C99 style variable declarations not supported by target compiler")
+else()
+add_subdirectory(monster)
+endif()
+if (FLATCC_REFLECTION)
+ add_subdirectory(reflection)
+endif()
diff --git a/flatcc/samples/bugreport/.gitignore b/flatcc/samples/bugreport/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/flatcc/samples/bugreport/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/flatcc/samples/bugreport/build.sh b/flatcc/samples/bugreport/build.sh
new file mode 100755
index 0000000..2de7528
--- /dev/null
+++ b/flatcc/samples/bugreport/build.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+cd $(dirname $0)
+
+FLATBUFFERS_DIR=../..
+NAME=myissue
+SCHEMA=eclectic.fbs
+OUT=build
+
+FLATCC_EXE=$FLATBUFFERS_DIR/bin/flatcc
+FLATCC_INCLUDE=$FLATBUFFERS_DIR/include
+FLATCC_LIB=$FLATBUFFERS_DIR/lib
+
+mkdir -p $OUT
+$FLATCC_EXE --outfile $OUT/${NAME}_generated.h -a $SCHEMA || exit 1
+cc -I$FLATCC_INCLUDE -g -o $OUT/$NAME $NAME.c -L$FLATCC_LIB -lflatccrt_d || exit 1
+echo "running $OUT/$NAME"
+if $OUT/$NAME; then
+ echo "success"
+else
+ echo "failed"
+ exit 1
+fi
diff --git a/flatcc/samples/bugreport/eclectic.fbs b/flatcc/samples/bugreport/eclectic.fbs
new file mode 100644
index 0000000..ad507e7
--- /dev/null
+++ b/flatcc/samples/bugreport/eclectic.fbs
@@ -0,0 +1,11 @@
+namespace Eclectic;
+
+enum Fruit : byte { Banana = -1, Orange = 42 }
+table FooBar {
+ meal : Fruit = Banana;
+ density : long (deprecated);
+ say : string;
+ height : short;
+}
+file_identifier "NOOB";
+root_type FooBar;
diff --git a/flatcc/samples/bugreport/myissue.c b/flatcc/samples/bugreport/myissue.c
new file mode 100644
index 0000000..0098235
--- /dev/null
+++ b/flatcc/samples/bugreport/myissue.c
@@ -0,0 +1,35 @@
+/* Minimal test with all headers generated into a single file. */
+#include "build/myissue_generated.h"
+#include "flatcc/support/hexdump.h"
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ void *buf;
+ size_t size;
+ flatcc_builder_t builder, *B;
+
+ (void)argc;
+ (void)argv;
+
+ B = &builder;
+ flatcc_builder_init(B);
+
+ Eclectic_FooBar_start_as_root(B);
+ Eclectic_FooBar_say_create_str(B, "hello");
+ Eclectic_FooBar_meal_add(B, Eclectic_Fruit_Orange);
+ Eclectic_FooBar_height_add(B, -8000);
+ Eclectic_FooBar_end_as_root(B);
+ buf = flatcc_builder_get_direct_buffer(B, &size);
+#if defined(PROVOKE_ERROR) || 0
+ /* Provoke error for testing. */
+ ((char*)buf)[0] = 42;
+#endif
+ ret = Eclectic_FooBar_verify_as_root(buf, size);
+ if (ret) {
+ hexdump("Eclectic.FooBar buffer for myissue", buf, size, stdout);
+ printf("could not verify Electic.FooBar table, got %s\n", flatcc_verify_error_string(ret));
+ }
+ flatcc_builder_clear(B);
+ return ret;
+}
diff --git a/flatcc/samples/monster/CMakeLists.txt b/flatcc/samples/monster/CMakeLists.txt
new file mode 100644
index 0000000..1c03455
--- /dev/null
+++ b/flatcc/samples/monster/CMakeLists.txt
@@ -0,0 +1,22 @@
+include(CTest)
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_fbs ALL)
+add_custom_command (
+ TARGET gen_monster_fbs
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli -a -o "${GEN_DIR}" "${FBS_DIR}/monster.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster.fbs"
+)
+add_executable(monster monster.c)
+add_dependencies(monster gen_monster_fbs)
+target_link_libraries(monster flatccrt)
+
+if (FLATCC_TEST)
+ add_test(monster monster${CMAKE_EXECUTABLE_SUFFIX})
+endif()
diff --git a/flatcc/samples/monster/build.sh b/flatcc/samples/monster/build.sh
new file mode 100755
index 0000000..e090aed
--- /dev/null
+++ b/flatcc/samples/monster/build.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+NAME=monster
+TMP=${ROOT}/build/tmp/samples/${NAME}
+EX=${ROOT}/samples/${NAME}
+
+CC=${CC:-cc}
+CFLAGS_DEBUG="-g -I ${ROOT}/include"
+CFLAGS_RELEASE="-O3 -DNDEBUG -I ${ROOT}/include"
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+bin/flatcc -a -o ${TMP} ${EX}/${NAME}.fbs
+
+cp ${EX}/*.c ${TMP}
+cd ${TMP}
+
+echo "building $NAME example (debug)"
+$CC $CFLAGS_DEBUG ${NAME}.c ${ROOT}/lib/libflatccrt_d.a -o ${NAME}_d
+echo "building $NAME example (release)"
+$CC $CFLAGS_RELEASE ${NAME}.c ${ROOT}/lib/libflatccrt.a -o ${NAME}
+
+echo "running $NAME example (debug)"
+./${NAME}_d
diff --git a/flatcc/samples/monster/monster.c b/flatcc/samples/monster/monster.c
new file mode 100644
index 0000000..32a39b0
--- /dev/null
+++ b/flatcc/samples/monster/monster.c
@@ -0,0 +1,353 @@
+// Example on how to build a Monster FlatBuffer.
+
+
+// Note: while some older C89 compilers are supported when
+// -DFLATCC_PORTABLE is defined, this particular sample is known not to
+// not work with MSVC 2010 (MSVC 2013 is OK) due to inline variable
+// declarations. These are easily move to the start of code blocks, but
+// since we follow the step-wise tutorial, it isn't really practical
+// in this case. The comment style is technically also in violation of C89.
+
+
+#include "monster_builder.h" // Generated by `flatcc`.
+// <string.h> and <assert.h> already included.
+
+// Convenient namespace macro to manage long namespace prefix.
+// The ns macro makes it possible to write `ns(Monster_create(...))`
+// instead of `MyGame_Sample_Monster_create(...)`
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
+
+// A helper to simplify creating vectors from C-arrays.
+#define c_vec_len(V) (sizeof(V)/sizeof((V)[0]))
+
+// This allows us to verify result in optimized builds.
+#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while(0)
+
+// Bottom-up approach where we create child objects and store these
+// in temporary references before a parent object is created with
+// these references.
+int create_monster_bottom_up(flatcc_builder_t *B, int flexible)
+{
+ flatbuffers_string_ref_t weapon_one_name = flatbuffers_string_create_str(B, "Sword");
+ int16_t weapon_one_damage = 3;
+
+ flatbuffers_string_ref_t weapon_two_name = flatbuffers_string_create_str(B, "Axe");
+ int16_t weapon_two_damage = 5;
+
+ // Use the `MyGame_Sample_Weapon_create` shortcut to create Weapons
+ // with all the fields set.
+ //
+ // In the C-API, verbs (here create) always follow the type name
+ // (here Weapon), prefixed by the namespace (here MyGame_Sample_):
+ // MyGame_Sample_Weapon_create(...), or ns(Weapone_create(...)).
+ 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));
+
+ // Serialize a name for our monster, called "Orc".
+ // The _str suffix indicates the source is an ascii-z string.
+ flatbuffers_string_ref_t name = flatbuffers_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};
+ flatbuffers_uint8_vec_ref_t inventory;
+ // `c_vec_len` is the convenience macro we defined earlier.
+ inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure));
+
+ // 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));
+
+
+ // Create a `Vec3`, representing the Orc's position in 3-D space.
+ ns(Vec3_t) pos = { 1.0f, 2.0f, 3.0f };
+
+
+ // Set his hit points to 300 and his mana to 150.
+ int16_t hp = 300;
+ // The default value is 150, so we will never store this field.
+ int16_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. Here we create
+ // a single union value that carries both the type and reference.
+ ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe));
+
+ if (!flexible) {
+ // Finally, create the monster using the `Monster_create` helper function
+ // to set all fields.
+ //
+ // Note that the Equipment union only take up one argument in C, where
+ // C++ takes a type and an object argument.
+ ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red),
+ weapons, equipped));
+
+ // Unlike C++ we do not use a Finish call. Instead we use the
+ // `create_as_root` action which has better type safety and
+ // simplicity.
+ //
+ // However, we can also express this as:
+ //
+ // ns(Monster_ref_t) orc = ns(Monster_create(B, ...));
+ // flatcc_builder_buffer_create(orc);
+ //
+ // In this approach the function should return the orc and
+ // let a calling function handle the flatcc_buffer_create call
+ // for a more composable setup that is also able to create child
+ // monsters. In general, `flatcc_builder` calls are best isolated
+ // in a containing driver function.
+
+ } else {
+
+ // A more flexible approach where we mix bottom-up and top-down
+ // style. We still create child objects first, but then create
+ // a top-down style monster object that we can manipulate in more
+ // detail.
+
+ // 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));
+
+ // We could also drop the `as_root` suffix from Monster_start/end(B)
+ // and add the table as buffer root later:
+ //
+ // ns(Monster_ref_t) orc = ns(Monster_start(B));
+ // ...
+ // ns(Monster_ref_t) orc = ns(Monster_end(B));
+ // flatcc_builder_buffer_create(orc);
+ //
+ // It is best to keep the `flatcc_builder` calls in a containing
+ // driver function for modularity.
+ }
+ return 0;
+}
+
+// Alternative top-down approach where parent objects are created before
+// their children. We only need to save one reference because the `axe`
+// object is used in two places effectively making the buffer object
+// graph a DAG.
+int create_monster_top_down(flatcc_builder_t *B)
+{
+ 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(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));
+ return 0;
+}
+
+// This isn't strictly needed because the builder already included the reader,
+// but we would need it if our reader were in a separate file.
+#include "monster_reader.h"
+
+#undef ns
+#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema.
+
+int access_monster_buffer(const void *buffer)
+{
+ // 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));
+
+ // Note: root object pointers are NOT the same as the `buffer` pointer.
+
+ // Make sure the buffer is accessible.
+ test_assert(monster != 0);
+
+ int16_t hp = ns(Monster_hp(monster));
+ int16_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));
+ size_t name_len = flatbuffers_string_len(name);
+
+ test_assert(hp == 300);
+ // Since 150 is the default, we are reading a value that wasn't stored.
+ test_assert(mana == 150);
+ test_assert(0 == strcmp(name, "Orc"));
+ test_assert(name_len == strlen("Orc"));
+
+ int hp_present = ns(Monster_hp_is_present(monster)); // 1
+ int mana_present = ns(Monster_mana_is_present(monster)); // 0
+ test_assert(hp_present);
+ test_assert(!mana_present);
+
+ ns(Vec3_struct_t) pos = ns(Monster_pos(monster));
+ // Make sure pos has been set.
+ test_assert(pos != 0);
+ float x = ns(Vec3_x(pos));
+ float y = ns(Vec3_y(pos));
+ float z = ns(Vec3_z(pos));
+
+ // The literal `f` suffix is important because double literals does
+ // not always map cleanly to 32-bit represention even with only a few digits:
+ // `1.0 == 1.0f`, but `3.2 != 3.2f`.
+ test_assert(x == 1.0f);
+ test_assert(y == 2.0f);
+ test_assert(z == 3.0f);
+
+ // We can also read the position into a C-struct. We have to copy
+ // because we generally do not know if the native endian format
+ // matches the one stored in the buffer (pe: protocol endian).
+ ns(Vec3_t) pos_vec;
+ // `pe` indicates endian conversion from protocol to native.
+ ns(Vec3_copy_from_pe(&pos_vec, pos));
+ test_assert(pos_vec.x == 1.0f);
+ test_assert(pos_vec.y == 2.0f);
+ test_assert(pos_vec.z == 3.0f);
+
+ // This is a const uint8_t *, but it shouldn't be accessed directly
+ // to ensure proper endian conversion. However, uint8 (ubyte) are
+ // not sensitive endianness, so we *could* have accessed it directly.
+ // The compiler likely optimizes this so that it doesn't matter.
+ flatbuffers_uint8_vec_t inv = ns(Monster_inventory(monster));
+ size_t inv_len = flatbuffers_uint8_vec_len(inv);
+ // Make sure the inventory has been set.
+ test_assert(inv != 0);
+ // If `inv` were absent, the length would 0, so the above test is redundant.
+ test_assert(inv_len == 10);
+ // Index 0 is the first, index 2 is the third.
+ // NOTE: C++ uses the `Get` terminology for vector elemetns, C use `at`.
+ uint8_t third_item = flatbuffers_uint8_vec_at(inv, 2);
+ test_assert(third_item == 2);
+
+ ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster));
+ size_t weapons_len = ns(Weapon_vec_len(weapons));
+ test_assert(weapons_len == 2);
+ // We can use `const char *` instead of `flatbuffers_string_t`.
+ const char *second_weapon_name = ns(Weapon_name(ns(Weapon_vec_at(weapons, 1))));
+ int16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1))));
+ test_assert(second_weapon_name != 0 && strcmp(second_weapon_name, "Axe") == 0);
+ test_assert(second_weapon_damage == 5);
+
+ // Access union type field.
+ if (ns(Monster_equipped_type(monster)) == ns(Equipment_Weapon)) {
+ // Cast to appropriate type:
+ // C does not require the cast to Weapon_table_t, but C++ does.
+ ns(Weapon_table_t) weapon = (ns(Weapon_table_t)) ns(Monster_equipped(monster));
+ const char *weapon_name = ns(Weapon_name(weapon));
+ int16_t weapon_damage = ns(Weapon_damage(weapon));
+
+ test_assert(0 == strcmp(weapon_name, "Axe"));
+ test_assert(weapon_damage == 5);
+ }
+ return 0;
+}
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ // Create a `FlatBufferBuilder`, which will be used to create our
+ // monsters' FlatBuffers.
+ flatcc_builder_t builder;
+ void *buf;
+ size_t size;
+
+ // Silence warnings.
+ (void)argc;
+ (void)argv;
+
+ // Initialize the builder object.
+ flatcc_builder_init(&builder);
+ test_assert(0 == create_monster_bottom_up(&builder, 0));
+
+ // Allocate and extract a readable buffer from internal builder heap.
+ // NOTE: Finalizing the buffer does NOT change the builder, it
+ // just creates a snapshot of the builder content.
+ // NOTE2: finalize_buffer uses malloc while finalize_aligned_buffer
+ // uses a portable aligned allocation method. Often the malloc
+ // version is sufficient, but won't work for all schema on all
+ // systems. If the buffer is written to disk or network, but not
+ // accessed in memory, `finalize_buffer` is also sufficient.
+ // The flatcc_builder version of free or aligned_free should be used
+ // instead of `free` although free will often work on POSIX systems.
+ // This ensures portability and prevents issues when linking to
+ // allocation libraries other than malloc.
+ buf = flatcc_builder_finalize_aligned_buffer(&builder, &size);
+ //buf = flatcc_builder_finalize_buffer(&builder, &size);
+
+ // We now have a FlatBuffer we can store on disk or send over a network.
+ // ** file/network code goes here :) **
+ // Instead, we're going to access it right away (as if we just received it).
+ //access_monster_buffer(buf);
+
+ // prior to v0.5.0, use `aligned_free`
+ flatcc_builder_aligned_free(buf);
+ //free(buf);
+ //
+ // The builder object can optionally be reused after a reset which
+ // is faster than creating a new builder. Subsequent use might
+ // entirely avoid temporary allocations until finalizing the buffer.
+ flatcc_builder_reset(&builder);
+ test_assert(0 == create_monster_bottom_up(&builder, 1));
+ buf = flatcc_builder_finalize_aligned_buffer(&builder, &size);
+ access_monster_buffer(buf);
+ flatcc_builder_aligned_free(buf);
+ flatcc_builder_reset(&builder);
+ create_monster_top_down(&builder);
+ buf = flatcc_builder_finalize_buffer(&builder, &size);
+ test_assert(0 == access_monster_buffer(buf));
+ flatcc_builder_free(buf);
+ // Eventually the builder must be cleaned up:
+ flatcc_builder_clear(&builder);
+
+ printf("The FlatBuffer was successfully created and accessed!\n");
+
+ return 0;
+}
diff --git a/flatcc/samples/monster/monster.fbs b/flatcc/samples/monster/monster.fbs
new file mode 100644
index 0000000..12859d2
--- /dev/null
+++ b/flatcc/samples/monster/monster.fbs
@@ -0,0 +1,32 @@
+// Example IDL file for our monster's schema.
+
+namespace MyGame.Sample;
+
+enum Color:byte { Red = 0, Green, Blue = 2 }
+
+union Equipment { Weapon } // Optionally add more tables.
+
+struct Vec3 {
+ x:float;
+ y:float;
+ z:float;
+}
+
+table Monster {
+ pos:Vec3; // Struct.
+ mana:short = 150;
+ hp:short = 100;
+ name:string;
+ friendly:bool = false (deprecated);
+ inventory:[ubyte]; // Vector of scalars.
+ color:Color = Blue; // Enum.
+ weapons:[Weapon]; // Vector of tables.
+ equipped:Equipment; // Union.
+}
+
+table Weapon {
+ name:string;
+ damage:short;
+}
+
+root_type Monster;
diff --git a/flatcc/samples/reflection/CMakeLists.txt b/flatcc/samples/reflection/CMakeLists.txt
new file mode 100644
index 0000000..cfbef1c
--- /dev/null
+++ b/flatcc/samples/reflection/CMakeLists.txt
@@ -0,0 +1,31 @@
+include(CTest)
+
+#
+# This projects depends headers generated from reflection.fbs but these
+# are pre-generated in `include/flatcc/reflection` so we don't need to
+# build them here.
+#
+# What we do build is a binary schema `monster.bfbs` for the monster
+# sample, and the actual C source of this project.
+#
+
+set(INC_DIR "${PROJECT_SOURCE_DIR}/include")
+set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(FBS_DIR "${PROJECT_SOURCE_DIR}/samples/monster")
+
+include_directories("${GEN_DIR}" "${INC_DIR}")
+
+add_custom_target(gen_monster_bfbs ALL)
+add_custom_command (
+ TARGET gen_monster_bfbs
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${GEN_DIR}"
+ COMMAND flatcc_cli --schema -o "${GEN_DIR}" "${FBS_DIR}/monster.fbs"
+ DEPENDS flatcc_cli "${FBS_DIR}/monster.fbs"
+)
+add_executable(bfbs2json bfbs2json.c)
+add_dependencies(bfbs2json gen_monster_bfbs)
+target_link_libraries(bfbs2json flatccrt)
+
+if (FLATCC_TEST)
+ add_test(bfbs2json bfbs2json${CMAKE_EXECUTABLE_SUFFIX} ${GEN_DIR}/monster.bfbs)
+endif()
diff --git a/flatcc/samples/reflection/bfbs2json.c b/flatcc/samples/reflection/bfbs2json.c
new file mode 100644
index 0000000..03c31e7
--- /dev/null
+++ b/flatcc/samples/reflection/bfbs2json.c
@@ -0,0 +1,314 @@
+#include "flatcc/support/readfile.h"
+#include "flatcc/reflection/reflection_reader.h"
+
+/* -DFLATCC_PORTABLE may help if inttypes.h is missing. */
+#ifndef PRId64
+#include <inttypes.h>
+#endif
+
+
+/*
+ * Reads a binary schema generated by `flatcc` or Googles `flatc` tool,
+ * then prints the content out in a custom JSON format.
+ *
+ * Note: This is completely unrelated to `flatcc's` JSON support - we
+ * just needed to do something tangible with the data we read from the
+ * binary schema and opted to print it as JSON.
+ *
+ * The JSON can be pretty printed with an external tool, for example:
+ *
+ * cat monster_test_schema.json | jq '.'
+ */
+
+void print_type(reflection_Type_table_t T)
+{
+ int first = 1;
+ printf("{");
+ if (reflection_Type_base_type_is_present(T)) {
+ if (!first) {
+ printf(",");
+ }
+ printf("\"base_type\":\"%s\"", reflection_BaseType_name(reflection_Type_base_type(T)));
+ first = 0;
+ }
+ if (reflection_Type_element_is_present(T)) {
+ if (!first) {
+ printf(",");
+ }
+ printf("\"element\":\"%s\"", reflection_BaseType_name(reflection_Type_element(T)));
+ first = 0;
+ }
+ if (reflection_Type_index_is_present(T)) {
+ if (!first) {
+ printf(",");
+ }
+ printf("\"index\":%d", reflection_Type_index(T));
+ first = 0;
+ }
+ if (reflection_Type_fixed_length_is_present(T)) {
+ if (!first) {
+ printf(",");
+ }
+ printf("\"fixed_length\":%d", reflection_Type_fixed_length(T));
+ first = 0;
+ }
+ printf("}");
+}
+
+void print_attributes(reflection_KeyValue_vec_t KV)
+{
+ size_t i;
+ reflection_KeyValue_table_t attribute;
+ const char *key, *value;
+
+ printf("[");
+ for (i = 0; i < reflection_KeyValue_vec_len(KV); ++i) {
+ attribute = reflection_KeyValue_vec_at(KV, i);
+ key = reflection_KeyValue_key(attribute);
+ value = reflection_KeyValue_value(attribute);
+ if (i > 0) {
+ printf(",");
+ }
+ printf("{\"key\":\"%s\"", key);
+ if (value) {
+ /* TODO: we ought to escape '\"' and other non-string-able characters. */
+ printf(",\"value\":\"%s\"", value);
+ }
+ printf("}");
+ }
+ printf("]");
+}
+
+void print_object(reflection_Object_table_t O)
+{
+ reflection_Field_vec_t Flds;
+ reflection_Field_table_t F;
+ size_t i;
+
+ Flds = reflection_Object_fields(O);
+ printf("{\"name\":\"%s\"", reflection_Object_name(O));
+ printf(",\"fields\":[");
+ for (i = 0; i < reflection_Field_vec_len(Flds); ++i) {
+ if (i > 0) {
+ printf(",");
+ }
+ F = reflection_Field_vec_at(Flds, i);
+ printf("{\"name\":\"%s\",\"type\":", reflection_Field_name(F));
+ print_type(reflection_Field_type(F));
+ if (reflection_Field_id_is_present(F)) {
+ printf(",\"id\":%hu", reflection_Field_id(F));
+ }
+ if (reflection_Field_default_integer_is_present(F)) {
+ printf(",\"default_integer\":%"PRId64"", (int64_t)reflection_Field_default_integer(F));
+ }
+ if (reflection_Field_default_real_is_present(F)) {
+ printf(",\"default_integer\":%lf", reflection_Field_default_real(F));
+ }
+ if (reflection_Field_required_is_present(F)) {
+ printf(",\"required\":%s", reflection_Field_required(F) ? "true" : "false");
+ }
+ if (reflection_Field_key_is_present(F)) {
+ printf(",\"key\":%s", reflection_Field_key(F) ? "true" : "false");
+ }
+ if (reflection_Field_attributes_is_present(F)) {
+ printf(",\"attributes\":");
+ print_attributes(reflection_Field_attributes(F));
+ }
+ printf("}");
+ }
+ printf("]");
+ if (reflection_Object_is_struct_is_present(O)) {
+ printf(",\"is_struct\":%s", reflection_Object_is_struct(O) ? "true" : "false");
+ }
+ if (reflection_Object_minalign_is_present(O)) {
+ printf(",\"minalign\":%d", reflection_Object_minalign(O));
+ }
+ if (reflection_Object_bytesize_is_present(O)) {
+ printf(",\"bytesize\":%d", reflection_Object_bytesize(O));
+ }
+ if (reflection_Object_attributes_is_present(O)) {
+ printf(",\"attributes\":");
+ print_attributes(reflection_Object_attributes(O));
+ }
+ printf("}");
+}
+
+void print_enum(reflection_Enum_table_t E)
+{
+ reflection_EnumVal_vec_t EnumVals;
+ reflection_EnumVal_table_t EV;
+ size_t i;
+
+ printf("{\"name\":\"%s\"", reflection_Enum_name(E));
+ EnumVals = reflection_Enum_values(E);
+ printf(",\"values\":[");
+ for (i = 0; i < reflection_Enum_vec_len(EnumVals); ++i) {
+ EV = reflection_EnumVal_vec_at(EnumVals, i);
+ if (i > 0) {
+ printf(",");
+ }
+ printf("{\"name\":\"%s\"", reflection_EnumVal_name(EV));
+ if (reflection_EnumVal_value_is_present(EV)) {
+ printf(",\"value\":%"PRId64"", (int64_t)reflection_EnumVal_value(EV));
+ }
+ if (reflection_EnumVal_object_is_present(EV)) {
+ printf(",\"object\":");
+ print_object(reflection_EnumVal_object(EV));
+ }
+ if (reflection_EnumVal_union_type_is_present(EV)) {
+ printf(",\"union_type\":");
+ print_type(reflection_EnumVal_union_type(EV));
+ }
+ printf("}");
+ }
+ printf("]");
+ if (reflection_Enum_is_union_is_present(E)) {
+ printf(",\"is_union\":%s", reflection_Enum_is_union(E) ? "true" : "false");
+ }
+ printf(",\"underlying_type\":");
+ print_type(reflection_Enum_underlying_type(E));
+ if (reflection_Enum_attributes_is_present(E)) {
+ printf(",\"attributes\":");
+ print_attributes(reflection_Enum_attributes(E));
+ }
+ printf("}");
+}
+
+void print_call(reflection_RPCCall_table_t C)
+{
+ printf("{\"name\":\"%s\"", reflection_RPCCall_name(C));
+ printf(",\"request\":");
+ print_object(reflection_RPCCall_request(C));
+ printf(",\"response\":");
+ print_object(reflection_RPCCall_response(C));
+
+ if (reflection_RPCCall_attributes_is_present(C)) {
+ printf(",\"attributes\":");
+ print_attributes(reflection_RPCCall_attributes(C));
+ }
+ printf("}");
+}
+
+void print_service(reflection_Service_table_t S)
+{
+ reflection_RPCCall_vec_t calls;
+ size_t i;
+
+ printf("{\"name\":\"%s\"", reflection_Service_name(S));
+
+ printf(",\"calls\":[");
+ calls = reflection_Service_calls(S);
+ for (i = 0; i < reflection_RPCCall_vec_len(calls); ++i) {
+ if (i > 0) {
+ printf(",");
+ }
+ print_call(reflection_RPCCall_vec_at(calls, i));
+ }
+ printf("]");
+
+ if (reflection_Service_attributes_is_present(S)) {
+ printf(",\"attributes\":");
+ print_attributes(reflection_Service_attributes(S));
+ }
+ printf("}");
+}
+
+void print_schema(reflection_Schema_table_t S)
+{
+ reflection_Object_vec_t Objs;
+ reflection_Enum_vec_t Enums;
+ reflection_Service_vec_t Services;
+ size_t i;
+
+ Objs = reflection_Schema_objects(S);
+ printf("{");
+ printf("\"objects\":[");
+ for (i = 0; i < reflection_Object_vec_len(Objs); ++i) {
+ if (i > 0) {
+ printf(",");
+ }
+ print_object(reflection_Object_vec_at(Objs, i));
+ }
+ printf("]");
+ Enums = reflection_Schema_enums(S);
+ printf(",\"enums\":[");
+ for (i = 0; i < reflection_Enum_vec_len(Enums); ++i) {
+ if (i > 0) {
+ printf(",");
+ }
+ print_enum(reflection_Enum_vec_at(Enums, i));
+ }
+ printf("]");
+ if (reflection_Schema_file_ident_is_present(S)) {
+ printf(",\"file_ident\":\"%s\"", reflection_Schema_file_ident(S));
+ }
+ if (reflection_Schema_file_ext_is_present(S)) {
+ printf(",\"file_ext\":\"%s\"", reflection_Schema_file_ext(S));
+ }
+ if (reflection_Schema_root_table_is_present(S)) {
+ printf(",\"root_table\":");
+ print_object(reflection_Schema_root_table(S));
+ }
+ if (reflection_Schema_services_is_present(S)) {
+ printf(",\"services\":[");
+ Services = reflection_Schema_services(S);
+ for (i = 0; i < reflection_Service_vec_len(Services); ++i) {
+ if (i > 0) {
+ printf(",");
+ }
+ print_service(reflection_Service_vec_at(Services, i));
+ }
+ printf("]");
+ }
+ printf("}\n");
+}
+
+int load_and_dump_schema(const char *filename)
+{
+ void *buffer;
+ size_t size;
+ int ret = -1;
+ reflection_Schema_table_t S;
+
+ buffer = readfile(filename, 100000, &size);
+ if (!buffer) {
+ fprintf(stderr, "failed to load binary schema file: '%s'\n", filename);
+ goto done;
+ }
+ if (size < 12) {
+ fprintf(stderr, "file too small to access: '%s'\n", filename);
+ goto done;
+ }
+ S = reflection_Schema_as_root(buffer);
+ if (!S) {
+ S = reflection_Schema_as_root((char*)buffer + 4);
+ if (S) {
+ fprintf(stderr, "(skipping length field of input buffer)\n");
+ }
+ }
+ if (!S) {
+ fprintf(stderr, "input is not a valid schema");
+ goto done;
+ }
+ print_schema(S);
+ ret = 0;
+
+done:
+ if (buffer) {
+ free(buffer);
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: bfbs2json <filename>\n");
+ fprintf(stderr, "reads a binary flatbuffer schema and prints it to compact json on stdout\n\n");
+ fprintf(stderr, "pretty print with exernal tool, for example:\n"
+ " bfbs2json myschema.bfbs | python -m json.tool > myschema.json\n"
+ "note: also understands binary schema files with a 4 byte length prefix\n");
+ exit(-1);
+ }
+ return load_and_dump_schema(argv[1]);
+}
diff --git a/flatcc/samples/reflection/build.sh b/flatcc/samples/reflection/build.sh
new file mode 100755
index 0000000..84ccf8a
--- /dev/null
+++ b/flatcc/samples/reflection/build.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -e
+cd `dirname $0`/../..
+ROOT=`pwd`
+TMP=${ROOT}/build/tmp/samples/reflection
+
+CC=${CC:-cc}
+${ROOT}/scripts/build.sh
+mkdir -p ${TMP}
+rm -rf ${TMP}/*
+#bin/flatcc --schema --schema-length=yes -o ${TMP} test/monster_test/monster_test.fbs
+bin/flatcc --schema -o ${TMP} test/monster_test/monster_test.fbs
+
+cp samples/reflection/*.c ${TMP}
+cd ${TMP}
+# We don't need debug version, but it is always useful to have if we get
+# assertions in the interface code.
+$CC -g -I ${ROOT}/include bfbs2json.c -o bfbs2jsond
+$CC -O3 -DNDEBUG -I ${ROOT}/include bfbs2json.c -o bfbs2json
+cp bfbs2json ${ROOT}/bin/bfbs2json
+echo "generating example json output from monster_test.fbs schema ..."
+${ROOT}/bin/bfbs2json ${TMP}/monster_test.bfbs > monster_test_schema.json
+cat monster_test_schema.json | python -m json.tool > pretty_monster_test_schema.json
+echo "test json file located in ${TMP}/monster_test_schema.json"
+echo "pretty printed file located in ${TMP}/pretty_monster_test_schema.json"
+echo "bfbs2json tool placed in ${ROOT}/bin/bfbs2json"