A feature that officially supports nested FlatBuffers.
authorWouter van Oortmerssen <wvo@google.com>
Wed, 16 Jul 2014 00:50:22 +0000 (17:50 -0700)
committerWouter van Oortmerssen <wvo@google.com>
Mon, 21 Jul 2014 23:40:39 +0000 (16:40 -0700)
Generates convenient accessors for the nested root.

Change-Id: Ic0b1531de7ace475ff2a7b1f430d27f41c838430
Tested: on Windows.

include/flatbuffers/flatbuffers.h
src/idl_gen_cpp.cpp
src/idl_parser.cpp
tests/MyGame/Example/Monster.go
tests/MyGame/Example/Monster.java
tests/monster_test.fbs
tests/monster_test_generated.h
tests/monsterdata_test.bin

index ab473b0..02f29e9 100644 (file)
@@ -262,15 +262,16 @@ public:
   iterator end() { return iterator(Data(), length_); }
   const_iterator end() const { return const_iterator(Data(), length_); }
 
+  // The raw data in little endian format. Use with care.
+  const uint8_t *Data() const {
+    return reinterpret_cast<const uint8_t *>(&length_ + 1);
+  }
+
 protected:
   // This class is only used to access pre-existing data. Don't ever
   // try to construct these manually.
   Vector();
 
-  const uint8_t *Data() const {
-    return reinterpret_cast<const uint8_t *>(&length_ + 1);
-  }
-
   uoffset_t length_;
 };
 
index cd560c5..8bb7551 100644 (file)
@@ -164,7 +164,8 @@ static void GenComment(const std::string &dc,
 }
 
 // Generate an accessor struct, builder structs & function for a table.
-static void GenTable(StructDef &struct_def, std::string *code_ptr) {
+static void GenTable(const Parser &parser, StructDef &struct_def,
+                     std::string *code_ptr) {
   if (struct_def.generated) return;
   std::string &code = *code_ptr;
 
@@ -191,6 +192,14 @@ static void GenTable(StructDef &struct_def, std::string *code_ptr) {
       if (IsScalar(field.value.type.base_type))
         code += ", " + field.value.constant;
       code += "); }\n";
+      auto nested = field.attributes.Lookup("nested_flatbuffer");
+      if (nested) {
+        auto nested_root = parser.structs_.Lookup(nested->constant);
+        assert(nested_root);  // Guaranteed to exist by parser.
+        code += "  const " + nested_root->name + " *" + field.name;
+        code += "_nested_root() { return flatbuffers::GetRoot<";
+        code += nested_root->name + ">(" + field.name + "()->Data()); }\n";
+      }
     }
   }
   // Generate a verifier function that can check a buffer from an untrusted
@@ -418,7 +427,7 @@ std::string GenerateCPP(const Parser &parser, const std::string &include_guard_i
   }
   for (auto it = parser.structs_.vec.begin();
        it != parser.structs_.vec.end(); ++it) {
-    if (!(**it).fixed) GenTable(**it, &decl_code);
+    if (!(**it).fixed) GenTable(parser, **it, &decl_code);
   }
 
   // Only output file-level code if there were any declarations.
index 8be0591..436d0bc 100644 (file)
@@ -333,6 +333,17 @@ void Parser::ParseField(StructDef &struct_def) {
   field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
   if (field.deprecated && struct_def.fixed)
     Error("can't deprecate fields in a struct");
+  auto nested = field.attributes.Lookup("nested_flatbuffer");
+  if (nested) {
+    if (nested->type.base_type != BASE_TYPE_STRING)
+      Error("nested_flatbuffer attribute must be a string (the root type)");
+    if (field.value.type.base_type != BASE_TYPE_VECTOR ||
+        field.value.type.element != BASE_TYPE_UCHAR)
+      Error("nested_flatbuffer attribute may only apply to a vector of ubyte");
+    // This will cause an error if the root type of the nested flatbuffer
+    // wasn't defined elsewhere.
+    LookupCreateStruct(nested->constant);
+  }
 
   if (typefield) {
     // If this field is a union, and it has a manually assigned id,
index 2bceae5..16894f1 100644 (file)
@@ -176,7 +176,24 @@ func (rcv *Monster) Enemy(obj *Monster) *Monster {
        return nil
 }
 
-func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(13) }
+func (rcv *Monster) Testnestedflatbuffer(j int) byte {
+       o := flatbuffers.UOffsetT(rcv._tab.Offset(30))
+       if o != 0 {
+               a := rcv._tab.Vector(o)
+               return rcv._tab.GetByte(a + flatbuffers.UOffsetT(j * 1))
+       }
+       return 0
+}
+
+func (rcv *Monster) TestnestedflatbufferLength() int {
+       o := flatbuffers.UOffsetT(rcv._tab.Offset(30))
+       if o != 0 {
+               return rcv._tab.VectorLen(o)
+       }
+       return 0
+}
+
+func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(14) }
 func MonsterAddPos(builder *flatbuffers.Builder, pos flatbuffers.UOffsetT) { builder.PrependStructSlot(0, flatbuffers.UOffsetT(pos), 0) }
 func MonsterAddMana(builder *flatbuffers.Builder, mana int16) { builder.PrependInt16Slot(1, mana, 150) }
 func MonsterAddHp(builder *flatbuffers.Builder, hp int16) { builder.PrependInt16Slot(2, hp, 100) }
@@ -193,4 +210,6 @@ func MonsterStartTestarrayofstringVector(builder *flatbuffers.Builder, numElems
 func MonsterAddTestarrayoftables(builder *flatbuffers.Builder, testarrayoftables flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(11, flatbuffers.UOffsetT(testarrayoftables), 0) }
 func MonsterStartTestarrayoftablesVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(4, numElems) }
 func MonsterAddEnemy(builder *flatbuffers.Builder, enemy flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(12, flatbuffers.UOffsetT(enemy), 0) }
+func MonsterAddTestnestedflatbuffer(builder *flatbuffers.Builder, testnestedflatbuffer flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(13, flatbuffers.UOffsetT(testnestedflatbuffer), 0) }
+func MonsterStartTestnestedflatbufferVector(builder *flatbuffers.Builder, numElems int) flatbuffers.UOffsetT { return builder.StartVector(1, numElems) }
 func MonsterEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
index 439dd7a..dcb7699 100755 (executable)
@@ -31,8 +31,10 @@ public class Monster extends Table {
   public int testarrayoftablesLength() { int o = __offset(26); return o != 0 ? __vector_len(o) : 0; }
   public Monster enemy() { return enemy(new Monster()); }
   public Monster enemy(Monster obj) { int o = __offset(28); return o != 0 ? obj.__init(__indirect(o + bb_pos), bb) : null; }
+  public byte testnestedflatbuffer(int j) { int o = __offset(30); return o != 0 ? bb.get(__vector(o) + j * 1) : 0; }
+  public int testnestedflatbufferLength() { int o = __offset(30); return o != 0 ? __vector_len(o) : 0; }
 
-  public static void startMonster(FlatBufferBuilder builder) { builder.startObject(13); }
+  public static void startMonster(FlatBufferBuilder builder) { builder.startObject(14); }
   public static void addPos(FlatBufferBuilder builder, int posOffset) { builder.addStruct(0, posOffset, 0); }
   public static void addMana(FlatBufferBuilder builder, short mana) { builder.addShort(1, mana, 150); }
   public static void addHp(FlatBufferBuilder builder, short hp) { builder.addShort(2, hp, 100); }
@@ -49,6 +51,8 @@ public class Monster extends Table {
   public static void addTestarrayoftables(FlatBufferBuilder builder, int testarrayoftablesOffset) { builder.addOffset(11, testarrayoftablesOffset, 0); }
   public static void startTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems); }
   public static void addEnemy(FlatBufferBuilder builder, int enemyOffset) { builder.addOffset(12, enemyOffset, 0); }
+  public static void addTestnestedflatbuffer(FlatBufferBuilder builder, int testnestedflatbufferOffset) { builder.addOffset(13, testnestedflatbufferOffset, 0); }
+  public static void startTestnestedflatbufferVector(FlatBufferBuilder builder, int numElems) { builder.startVector(1, numElems); }
   public static int endMonster(FlatBufferBuilder builder) { return builder.endObject(); }
 };
 
index cf191c4..9e6d444 100755 (executable)
@@ -32,6 +32,7 @@ table Monster {
   enemy:Monster (id:12);
   test:Any (id: 8);
   test4:[Test] (id: 9);
+  testnestedflatbuffer:[ubyte] (id:13, nested_flatbuffer: "Monster");
 }
 
 root_type Monster;
index b8ad91c..e6beb77 100755 (executable)
@@ -93,6 +93,8 @@ struct Monster : private flatbuffers::Table {
   /// an example documentation comment: this will end up in the generated code multiline too
   const flatbuffers::Vector<flatbuffers::Offset<Monster>> *testarrayoftables() const { return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Monster>> *>(26); }
   const Monster *enemy() const { return GetPointer<const Monster *>(28); }
+  const flatbuffers::Vector<uint8_t> *testnestedflatbuffer() const { return GetPointer<const flatbuffers::Vector<uint8_t> *>(30); }
+  const Monster *testnestedflatbuffer_nested_root() { return flatbuffers::GetRoot<Monster>(testnestedflatbuffer()->Data()); }
   bool Verify(const flatbuffers::Verifier &verifier) const {
     return VerifyTable(verifier) &&
            VerifyField<Vec3>(verifier, 4 /* pos */) &&
@@ -115,7 +117,9 @@ struct Monster : private flatbuffers::Table {
            verifier.Verify(testarrayoftables()) &&
            verifier.VerifyVectorOfTables(testarrayoftables()) &&
            VerifyField<flatbuffers::uoffset_t>(verifier, 28 /* enemy */) &&
-           verifier.VerifyTable(enemy());
+           verifier.VerifyTable(enemy()) &&
+           VerifyField<flatbuffers::uoffset_t>(verifier, 30 /* testnestedflatbuffer */) &&
+           verifier.Verify(testnestedflatbuffer());
   }
 };
 
@@ -134,9 +138,10 @@ struct MonsterBuilder {
   void add_testarrayofstring(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring) { fbb_.AddOffset(24, testarrayofstring); }
   void add_testarrayoftables(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables) { fbb_.AddOffset(26, testarrayoftables); }
   void add_enemy(flatbuffers::Offset<Monster> enemy) { fbb_.AddOffset(28, enemy); }
+  void add_testnestedflatbuffer(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testnestedflatbuffer) { fbb_.AddOffset(30, testnestedflatbuffer); }
   MonsterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); }
   MonsterBuilder &operator=(const MonsterBuilder &);
-  flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 13)); }
+  flatbuffers::Offset<Monster> Finish() { return flatbuffers::Offset<Monster>(fbb_.EndTable(start_, 14)); }
 };
 
 inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb,
@@ -151,8 +156,10 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
    flatbuffers::Offset<flatbuffers::Vector<const Test *>> test4 = 0,
    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> testarrayofstring = 0,
    flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Monster>>> testarrayoftables = 0,
-   flatbuffers::Offset<Monster> enemy = 0) {
+   flatbuffers::Offset<Monster> enemy = 0,
+   flatbuffers::Offset<flatbuffers::Vector<uint8_t>> testnestedflatbuffer = 0) {
   MonsterBuilder builder_(_fbb);
+  builder_.add_testnestedflatbuffer(testnestedflatbuffer);
   builder_.add_enemy(enemy);
   builder_.add_testarrayoftables(testarrayoftables);
   builder_.add_testarrayofstring(testarrayofstring);
index f09bf50..6313745 100644 (file)
Binary files a/tests/monsterdata_test.bin and b/tests/monsterdata_test.bin differ