Added optional object based API for C++.
authorWouter van Oortmerssen <wvo@google.com>
Sat, 2 Jul 2016 01:08:51 +0000 (18:08 -0700)
committerWouter van Oortmerssen <wvo@google.com>
Wed, 20 Jul 2016 21:58:57 +0000 (14:58 -0700)
Change-Id: If927f3ea3fb3723088fa287f24bdd1ad43c8d1d1
Tested: on Linux.

16 files changed:
.gitignore
CMakeLists.txt
docs/source/Compiler.md
docs/source/CppUsage.md
docs/source/Tutorial.md
include/flatbuffers/flatbuffers.h
include/flatbuffers/idl.h
samples/monster_generated.h
src/flatc.cpp
src/idl_gen_cpp.cpp
tests/generate_code.bat
tests/generate_code.sh
tests/monster_test_generated.h
tests/namespace_test/namespace_test1_generated.h
tests/namespace_test/namespace_test2_generated.h
tests/test.cpp

index 6f3894d..a51ef47 100755 (executable)
@@ -40,6 +40,8 @@ flatsamplebinary
 flatsamplebinary.exe
 flatsampletext
 flatsampletext.exe
+grpctest
+grpctest.exe
 snapshot.sh
 tests/go_gen
 tests/monsterdata_java_wire.mon
index c2dd950..cfb84ba 100644 (file)
@@ -6,8 +6,10 @@ project(FlatBuffers)
 option(FLATBUFFERS_CODE_COVERAGE "Enable the code coverage build option." OFF)
 option(FLATBUFFERS_BUILD_TESTS "Enable the build of tests and samples." ON)
 option(FLATBUFFERS_INSTALL "Enable the installation of targets." ON)
-option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" ON)
-option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" ON)
+option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library"
+       ON)
+option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler"
+       ON)
 option(FLATBUFFERS_BUILD_FLATHASH "Enable the build of flathash" ON)
 option(FLATBUFFERS_BUILD_GRPCTEST "Enable the build of grpctest" OFF)
 
@@ -95,7 +97,8 @@ set(FlatBuffers_GRPCTest_SRCS
 
 if(APPLE)
   set(CMAKE_CXX_FLAGS
-    "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror -Wextra")
+    "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror
+                        -Wextra")
 elseif(CMAKE_COMPILER_IS_GNUCXX)
   if(CYGWIN)
     set(CMAKE_CXX_FLAGS
@@ -118,7 +121,8 @@ elseif(CMAKE_COMPILER_IS_GNUCXX)
 
 elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
   set(CMAKE_CXX_FLAGS
-      "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror -Wextra")
+      "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror
+                          -Wextra")
   if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD")
     set(CMAKE_EXE_LINKER_FLAGS
         "${CMAKE_EXE_LINKER_FLAGS} -lc++abi")
@@ -165,7 +169,9 @@ function(compile_flatbuffers_schema_to_cpp SRC_FBS)
   string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS})
   add_custom_command(
     OUTPUT ${GEN_HEADER}
-    COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
+    COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable
+            --gen-object-api -o "${SRC_FBS_DIR}"
+            "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
     DEPENDS flatc)
 endfunction()
 
@@ -174,7 +180,8 @@ function(compile_flatbuffers_schema_to_binary SRC_FBS)
   string(REGEX REPLACE "\\.fbs$" ".bfbs" GEN_BINARY_SCHEMA ${SRC_FBS})
   add_custom_command(
     OUTPUT ${GEN_BINARY_SCHEMA}
-    COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
+    COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}"
+            "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}"
     DEPENDS flatc)
 endfunction()
 
index ad584c7..1ff2c8d 100755 (executable)
@@ -81,6 +81,11 @@ Additional options:
 -   `--gen-mutable` : Generate additional non-const accessors for mutating
     FlatBuffers in-place.
 
+    `--gen-object-api` : Generate an additional object-based API. This API is
+    more convenient for object construction and mutation than the base API,
+    at the cost of efficiency (object allocation). Recommended only to be used
+    if other options are insufficient.
+
 -   `--gen-onefile` :  Generate single output file (useful for C#)
 
 -   `--gen-all`: Generate not just code for the current schema files, but
index d4abe66..060432b 100755 (executable)
@@ -85,6 +85,27 @@ convenient accessors for all fields, e.g. `hp()`, `mana()`, etc:
 
 *Note: That we never stored a `mana` value, so it will return the default.*
 
+## Object based API.
+
+FlatBuffers is all about memory efficiency, which is why its base API is written
+around using as little as possible of it. This does make the API clumsier
+(requiring pre-order construction of all data, and making mutation harder).
+
+For times when efficiency is less important a more convenient object based API
+can be used (through `--gen-object-api`) that is able to unpack & pack a
+FlatBuffer into objects and standard STL containers, allowing for convenient
+construction, access and mutation.
+
+To use:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
+    auto monsterobj = GetMonster(buffer)->UnPack();
+    cout << monsterobj->name;  // This is now a std::string!
+    monsterobj->name = "Bob";  // Change the name.
+    FlatBufferBuilder fbb;
+    monsterobj->Pack(fbb);     // Serialize into new buffer.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 ## Reflection (& Resizing)
 
 There is experimental support for reflection in FlatBuffers, allowing you to
index 6f6ac9a..c457742 100644 (file)
@@ -1881,6 +1881,9 @@ One way to solve this is to call `ForceDefaults` on a FlatBufferBuilder to
 force all fields you set to actually be written. This, of course, increases the
 size of the buffer somewhat, but this may be acceptable for a mutable buffer.
 
+If this is not sufficient, other ways of mutating FlatBuffers may be supported
+in your language through an object based API (`--gen-object-api`) or reflection.
+See the individual language documents for support.
 
 ## JSON with FlatBuffers
 
index 01ab6db..a764d86 100644 (file)
@@ -984,6 +984,17 @@ FLATBUFFERS_FINAL_CLASS
     return CreateVector(v.data(), v.size());
   }
 
+  // vector<bool> may be implemented using a bit-set, so we can't access it as
+  // an array. Instead, read elements manually.
+  // Background: https://isocpp.org/blog/2012/11/on-vectorbool
+  Offset<Vector<uint8_t>> CreateVector(const std::vector<bool> &v) {
+    StartVector(v.size(), sizeof(uint8_t));
+    for (auto i = v.size(); i > 0; ) {
+      PushElement(static_cast<uint8_t>(v[--i]));
+    }
+    return Offset<Vector<uint8_t>>(EndVector(v.size()));
+  }
+
   /// @brief Serialize values returned by a function into a FlatBuffer `vector`.
   /// This is a convenience function that takes care of iteration for you.
   /// @tparam T The data type of the `std::vector` elements.
@@ -1523,6 +1534,12 @@ class Table {
   uint8_t data_[1];
 };
 
+// Base class for native objects (FlatBuffer data de-serialized into native
+// C++ data structures).
+// Contains no functionality, purely documentative.
+struct NativeTable {
+};
+
 // Helper function to test if a field is present, using any of the field
 // enums in the generated code.
 // `table` must be a generated table type. Since this is a template parameter,
index dddfab2..a3027a0 100644 (file)
@@ -338,7 +338,8 @@ struct IDLOptions {
   bool skip_unexpected_fields_in_json;
   bool generate_name_strings;
   bool escape_proto_identifiers;
-  
+  bool generate_object_based_api;
+
   // Possible options for the more general generator below.
   enum Language { kJava, kCSharp, kGo, kMAX };
 
@@ -358,6 +359,7 @@ struct IDLOptions {
       skip_unexpected_fields_in_json(false),
       generate_name_strings(false),
       escape_proto_identifiers(false),
+      generate_object_based_api(false),
       lang(IDLOptions::kJava) {}
 };
 
index 62a23ad..636aa37 100644 (file)
@@ -11,8 +11,10 @@ namespace Sample {
 struct Vec3;
 
 struct Monster;
+struct MonsterT;
 
 struct Weapon;
+struct WeaponT;
 
 enum Color {
   Color_Red = 0,
@@ -36,6 +38,21 @@ enum Equipment {
   Equipment_MAX = Equipment_Weapon
 };
 
+struct EquipmentUnion {
+  Equipment type;
+
+  flatbuffers::NativeTable *table;
+  EquipmentUnion() : type(Equipment_NONE), table(nullptr) {}
+  EquipmentUnion(const EquipmentUnion &);
+  EquipmentUnion &operator=(const EquipmentUnion &);
+  ~EquipmentUnion();
+
+  static flatbuffers::NativeTable *UnPack(const void *union_obj, Equipment type);
+  flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb) const;
+
+  WeaponT *AsWeapon() { return type == Equipment_Weapon ? reinterpret_cast<WeaponT *>(table) : nullptr; }
+};
+
 inline const char **EnumNamesEquipment() {
   static const char *names[] = { "NONE", "Weapon", nullptr };
   return names;
@@ -52,6 +69,8 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS {
   float z_;
 
  public:
+  Vec3() { memset(this, 0, sizeof(Vec3)); }
+  Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); }
   Vec3(float _x, float _y, float _z)
     : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)) { }
 
@@ -64,6 +83,17 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS {
 };
 STRUCT_END(Vec3, 12);
 
+struct MonsterT : public flatbuffers::NativeTable {
+  std::unique_ptr<Vec3> pos;
+  int16_t mana;
+  int16_t hp;
+  std::string name;
+  std::vector<uint8_t> inventory;
+  Color color;
+  std::vector<std::unique_ptr<WeaponT>> weapons;
+  EquipmentUnion equipped;
+};
+
 struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum {
     VT_POS = 4,
@@ -112,6 +142,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
            VerifyEquipment(verifier, equipped(), equipped_type()) &&
            verifier.EndTable();
   }
+  std::unique_ptr<MonsterT> UnPack() const;
 };
 
 struct MonsterBuilder {
@@ -170,6 +201,13 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
   return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector<uint8_t>(*inventory), color, weapons ? 0 : _fbb.CreateVector<flatbuffers::Offset<Weapon>>(*weapons), equipped_type, equipped);
 }
 
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o);
+
+struct WeaponT : public flatbuffers::NativeTable {
+  std::string name;
+  int16_t damage;
+};
+
 struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum {
     VT_NAME = 4,
@@ -186,6 +224,7 @@ struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
            VerifyField<int16_t>(verifier, VT_DAMAGE) &&
            verifier.EndTable();
   }
+  std::unique_ptr<WeaponT> UnPack() const;
 };
 
 struct WeaponBuilder {
@@ -216,6 +255,48 @@ inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &
   return CreateWeapon(_fbb, name ? 0 : _fbb.CreateString(name), damage);
 }
 
+inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o);
+
+inline std::unique_ptr<MonsterT> Monster::UnPack() const {
+  auto _o = new MonsterT();
+  { auto _e = pos(); if (_e) _o->pos = std::unique_ptr<Vec3>(new Vec3(*_e)); };
+  { auto _e = mana(); _o->mana = _e; };
+  { auto _e = hp(); _o->hp = _e; };
+  { auto _e = name(); if (_e) _o->name = _e->str(); };
+  { auto _e = inventory(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } };
+  { auto _e = color(); _o->color = _e; };
+  { auto _e = weapons(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->weapons.push_back(_e->Get(_i)->UnPack()); } } };
+  { auto _e = equipped_type(); _o->equipped.type = _e; };
+  { auto _e = equipped(); if (_e) _o->equipped.table = EquipmentUnion::UnPack(_e, equipped_type()); };
+  return std::unique_ptr<MonsterT>(_o);
+}
+
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) {
+  return CreateMonster(_fbb,
+    _o->pos ? _o->pos.get() : 0,
+    _o->mana,
+    _o->hp,
+    _o->name.size() ? _fbb.CreateString(_o->name) : 0,
+    _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0,
+    _o->color,
+    _o->weapons.size() ? _fbb.CreateVector<flatbuffers::Offset<Weapon>>(_o->weapons.size(), [&](size_t i) { return CreateWeapon(_fbb, _o->weapons[i].get()); }) : 0,
+    _o->equipped.type,
+    _o->equipped.Pack(_fbb));
+}
+
+inline std::unique_ptr<WeaponT> Weapon::UnPack() const {
+  auto _o = new WeaponT();
+  { auto _e = name(); if (_e) _o->name = _e->str(); };
+  { auto _e = damage(); _o->damage = _e; };
+  return std::unique_ptr<WeaponT>(_o);
+}
+
+inline flatbuffers::Offset<Weapon> CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o) {
+  return CreateWeapon(_fbb,
+    _o->name.size() ? _fbb.CreateString(_o->name) : 0,
+    _o->damage);
+}
+
 inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_obj, Equipment type) {
   switch (type) {
     case Equipment_NONE: return true;
@@ -224,6 +305,29 @@ inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_o
   }
 }
 
+inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *union_obj, Equipment type) {
+  switch (type) {
+    case Equipment_NONE: return nullptr;
+    case Equipment_Weapon: return reinterpret_cast<const Weapon *>(union_obj)->UnPack().release();
+    default: return nullptr;
+  }
+}
+
+inline flatbuffers::Offset<void> EquipmentUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const {
+  switch (type) {
+    case Equipment_NONE: return 0;
+    case Equipment_Weapon: return CreateWeapon(_fbb, reinterpret_cast<const WeaponT *>(table)).Union();
+    default: return 0;
+  }
+}
+
+inline EquipmentUnion::~EquipmentUnion() {
+  switch (type) {
+    case Equipment_Weapon: delete reinterpret_cast<WeaponT *>(table); break;
+    default:;
+  }
+}
+
 inline const MyGame::Sample::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Sample::Monster>(buf); }
 
 inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot<Monster>(buf); }
index d410718..631eaf1 100644 (file)
@@ -123,6 +123,7 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) {
       "  --gen-onefile      Generate single output file for C#.\n"
       "  --gen-name-strings Generate type name functions for C++.\n"
       "  --escape-proto-ids Disable appending '_' in namespaces names.\n"
+      "  --gen-object-api   Generate an additional object-based API\n"
       "  --raw-binary       Allow binaries without file_indentifier to be read.\n"
       "                     This may crash flatc given a mismatched schema.\n"
       "  --proto            Input is a .proto, translate to .fbs.\n"
@@ -179,6 +180,8 @@ int main(int argc, const char *argv[]) {
         opts.mutable_buffer = true;
       } else if(arg == "--gen-name-strings") {
         opts.generate_name_strings = true;
+      } else if(arg == "--gen-object-api") {
+        opts.generate_object_based_api = true;
       } else if(arg == "--gen-all") {
         opts.generate_all = true;
         opts.include_dependence_headers = false;
index e12e8a3..1479ad9 100644 (file)
@@ -96,7 +96,11 @@ class CppGenerator : public BaseGenerator {
       auto &struct_def = **it;
       if (!struct_def.generated) {
         SetNameSpace(struct_def.defined_namespace, &code);
-        code += "struct " + struct_def.name + ";\n\n";
+        code += "struct " + struct_def.name + ";\n";
+        if (parser_.opts.generate_object_based_api && !struct_def.fixed) {
+          code += "struct " + NativeName(struct_def.name) + ";\n";
+        }
+        code += "\n";
       }
     }
 
@@ -127,6 +131,14 @@ class CppGenerator : public BaseGenerator {
         GenTable(struct_def, &code);
       }
     }
+    for (auto it = parser_.structs_.vec.begin();
+         it != parser_.structs_.vec.end(); ++it) {
+      auto &struct_def = **it;
+      if (!struct_def.fixed && !struct_def.generated) {
+        SetNameSpace(struct_def.defined_namespace, &code);
+        GenTablePost(struct_def, &code);
+      }
+    }
 
     // Generate code for union verifiers.
     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
@@ -134,7 +146,7 @@ class CppGenerator : public BaseGenerator {
       auto &enum_def = **it;
       if (enum_def.is_union && !enum_def.generated) {
         SetNameSpace(enum_def.defined_namespace, &code);
-        GenEnumPost(enum_def, &code);
+        GenUnionPost(enum_def, &code);
       }
     }
 
@@ -232,9 +244,10 @@ class CppGenerator : public BaseGenerator {
   // Return a C++ type from the table in idl.h
   std::string GenTypeBasic(const Type &type, bool user_facing_type) {
     static const char *ctypename[] = {
-#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) #CTYPE,
+    #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \
+            #CTYPE,
         FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
-#undef FLATBUFFERS_TD
+    #undef FLATBUFFERS_TD
     };
     if (user_facing_type) {
       if (type.enum_def) return WrapInNameSpace(*type.enum_def);
@@ -252,9 +265,8 @@ class CppGenerator : public BaseGenerator {
       case BASE_TYPE_VECTOR:
         return "flatbuffers::Vector<" +
                GenTypeWire(type.VectorType(), "", false) + ">";
-      case BASE_TYPE_STRUCT: {
+      case BASE_TYPE_STRUCT:
         return WrapInNameSpace(*type.struct_def);
-      }
       case BASE_TYPE_UNION:
       // fall through
       default:
@@ -283,6 +295,34 @@ class CppGenerator : public BaseGenerator {
                                 : "flatbuffers::uoffset_t";
   }
 
+  // TODO(wvo): make this configurable.
+  std::string NativeName(const std::string &name) { return name + "T"; }
+
+  std::string GenTypeNative(const Type &type, bool invector) {
+    switch (type.base_type) {
+      case BASE_TYPE_STRING:
+        return "std::string";
+      case BASE_TYPE_VECTOR:
+        return "std::vector<" + GenTypeNative(type.VectorType(), true) + ">";
+      case BASE_TYPE_STRUCT:
+        if (IsStruct(type)) {
+          if (invector) {
+            return WrapInNameSpace(*type.struct_def);
+          } else {
+            return "std::unique_ptr<" +
+                   WrapInNameSpace(*type.struct_def) + ">";
+          }
+        } else {
+          return "std::unique_ptr<" +
+                 NativeName(WrapInNameSpace(*type.struct_def)) + ">";
+        }
+      case BASE_TYPE_UNION:
+        return type.enum_def->name + "Union";
+      default:
+        return GenTypeBasic(type, true);
+    }
+  }
+
   // Return a C++ type for any type (scalar/pointer) specifically for
   // using a flatbuffer.
   std::string GenTypeGet(const Type &type,
@@ -316,12 +356,37 @@ class CppGenerator : public BaseGenerator {
     }
   }
 
-  std::string EnumSignature(EnumDef &enum_def) {
+  std::string UnionVerifySignature(EnumDef &enum_def) {
     return "inline bool Verify" + enum_def.name +
-           "(flatbuffers::Verifier &verifier, " + "const void *union_obj, " +
+           "(flatbuffers::Verifier &verifier, const void *union_obj, " +
            enum_def.name + " type)";
   }
 
+  std::string UnionUnPackSignature(EnumDef &enum_def, bool inclass) {
+    return (inclass ? "static " : "") +
+           std::string("flatbuffers::NativeTable *") +
+           (inclass ? "" : enum_def.name + "Union::") +
+           "UnPack(const void *union_obj, " + enum_def.name + " type)";
+  }
+
+  std::string UnionPackSignature(EnumDef &enum_def, bool inclass) {
+    return "flatbuffers::Offset<void> " +
+           (inclass ? "" : enum_def.name + "Union::") +
+           "Pack(flatbuffers::FlatBufferBuilder &_fbb) const";
+  }
+
+  std::string TableCreateSignature(StructDef &struct_def) {
+    return "inline flatbuffers::Offset<" + struct_def.name + "> Create" +
+           struct_def.name  +
+           "(flatbuffers::FlatBufferBuilder &_fbb, const " +
+           NativeName(struct_def.name) + " *_o)";
+  }
+
+  std::string TableUnPackSignature(StructDef &struct_def, bool inclass) {
+    return "std::unique_ptr<" + NativeName(struct_def.name) + "> " +
+           (inclass ? "" : struct_def.name + "::") + "UnPack() const";
+  }
+
   // Generate an enum declaration and an enum string lookup table.
   void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
@@ -363,6 +428,36 @@ class CppGenerator : public BaseGenerator {
               GenTypeBasic(enum_def.underlying_type, false) + ")\n";
     code += "\n";
 
+    if (parser_.opts.generate_object_based_api && enum_def.is_union) {
+      // Generate a union type
+      code += "struct " + enum_def.name + "Union {\n";
+      code += "  " + enum_def.name + " type;\n\n";
+      code += "  flatbuffers::NativeTable *table;\n";
+      code += "  " + enum_def.name + "Union() : type(";
+      code += GenEnumVal(enum_def, "NONE", parser_.opts);
+      code += "), table(nullptr) {}\n";
+      code += "  " + enum_def.name + "Union(const ";
+      code += enum_def.name + "Union &);\n";
+      code += "  " + enum_def.name + "Union &operator=(const ";
+      code += enum_def.name + "Union &);\n";
+      code += "  ~" + enum_def.name + "Union();\n\n";
+      code += "  " + UnionUnPackSignature(enum_def, true) + ";\n";
+      code += "  " + UnionPackSignature(enum_def, true) + ";\n\n";
+      for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+           ++it) {
+        auto &ev = **it;
+        if (ev.value) {
+          auto native_name = NativeName(WrapInNameSpace(*ev.struct_def));
+          code += "  " + native_name + " *As";
+          code += ev.name + "() { return type == ";
+          code += GetEnumVal(enum_def, ev, parser_.opts);
+          code += " ? reinterpret_cast<" + native_name;
+          code += " *>(table) : nullptr; }\n";
+        }
+      }
+      code += "};\n\n";
+    }
+
     // Generate a generate string table for enum values.
     // Problem is, if values are very sparse that could generate really big
     // tables. Ideally in that case we generate a map lookup instead, but for
@@ -395,31 +490,82 @@ class CppGenerator : public BaseGenerator {
     }
 
     if (enum_def.is_union) {
-      code += EnumSignature(enum_def) + ";\n\n";
+      code += UnionVerifySignature(enum_def) + ";\n\n";
     }
   }
 
-  void GenEnumPost(EnumDef &enum_def, std::string *code_ptr_post) {
+  void GenUnionPost(EnumDef &enum_def, std::string *code_ptr) {
     // Generate a verifier function for this union that can be called by the
     // table verifier functions. It uses a switch case to select a specific
     // verifier function to call, this should be safe even if the union type
     // has been corrupted, since the verifiers will simply fail when called
     // on the wrong type.
-    std::string &code_post = *code_ptr_post;
-    code_post += EnumSignature(enum_def) + " {\n  switch (type) {\n";
+    std::string &code = *code_ptr;
+    code += UnionVerifySignature(enum_def) + " {\n  switch (type) {\n";
     for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
          ++it) {
       auto &ev = **it;
-      code_post += "    case " + GetEnumVal(enum_def, ev, parser_.opts);
+      code += "    case " + GetEnumVal(enum_def, ev, parser_.opts);
       if (!ev.value) {
-        code_post += ": return true;\n";  // "NONE" enum value.
+        code += ": return true;\n";  // "NONE" enum value.
       } else {
-        code_post += ": return verifier.VerifyTable(reinterpret_cast<const ";
-        code_post += WrapInNameSpace(*ev.struct_def);
-        code_post += " *>(union_obj));\n";
+        code += ": return verifier.VerifyTable(reinterpret_cast<const ";
+        code += WrapInNameSpace(*ev.struct_def);
+        code += " *>(union_obj));\n";
+      }
+    }
+    code += "    default: return false;\n  }\n}\n\n";
+
+    if (parser_.opts.generate_object_based_api) {
+      // Generate a union pack & unpack function.
+      code += "inline " + UnionUnPackSignature(enum_def, false);
+      code += " {\n  switch (type) {\n";
+      for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+           ++it) {
+        auto &ev = **it;
+        code += "    case " + GetEnumVal(enum_def, ev, parser_.opts);
+        if (!ev.value) {
+          code += ": return nullptr;\n";  // "NONE" enum value.
+        } else {
+          code += ": return reinterpret_cast<const ";
+          code += WrapInNameSpace(*ev.struct_def);
+          code += " *>(union_obj)->UnPack().release();\n";
+        }
+      }
+      code += "    default: return nullptr;\n  }\n}\n\n";
+      code += "inline " + UnionPackSignature(enum_def, false);
+      code += " {\n  switch (type) {\n";
+      for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+           ++it) {
+        auto &ev = **it;
+        code += "    case " + GetEnumVal(enum_def, ev, parser_.opts);
+        if (!ev.value) {
+          code += ": return 0;\n";  // "NONE" enum value.
+        } else {
+          code += ": return Create" + ev.struct_def->name;
+          code += "(_fbb, reinterpret_cast<const ";
+          code += NativeName(WrapInNameSpace(*ev.struct_def));
+          code += " *>(table)).Union();\n";
+        }
+      }
+      code += "    default: return 0;\n  }\n}\n\n";
+
+      // Generate a union destructor.
+      code += "inline " + enum_def.name + "Union::~";
+      code += enum_def.name + "Union() {\n";
+      code += "  switch (type) {\n";
+      for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
+           ++it) {
+        auto &ev = **it;
+        if (ev.value) {
+          code += "    case " + GenEnumVal(enum_def, ev.name, parser_.opts);
+          code += ": delete reinterpret_cast<";
+          code += NativeName(WrapInNameSpace(*ev.struct_def));
+          code += " *>(table); break;\n";
+        }
       }
+      code += "    default:;\n  }\n}\n\n";
     }
-    code_post += "    default: return false;\n  }\n}\n\n";
   }
 
   // Generates a value with optionally a cast applied if the field has a
@@ -491,6 +637,24 @@ class CppGenerator : public BaseGenerator {
   // Generate an accessor struct, builder structs & function for a table.
   void GenTable(StructDef &struct_def, std::string *code_ptr) {
     std::string &code = *code_ptr;
+
+    if (parser_.opts.generate_object_based_api) {
+      // Generate a C++ object that can hold an unpacked version of this
+      // table.
+      code += "struct " + NativeName(struct_def.name);
+      code += " : public flatbuffers::NativeTable {\n";
+      for (auto it = struct_def.fields.vec.begin();
+           it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (!field.deprecated &&  // Deprecated fields won't be accessible.
+            field.value.type.base_type != BASE_TYPE_UTYPE) {
+          code += "  " + GenTypeNative(field.value.type, false) + " ";
+          code += field.name + ";\n";
+        }
+      }
+      code += "};\n\n";
+    }
+
     // Generate an accessor struct, with methods of the form:
     // type name() const { return GetField<type>(offset, defaultval); }
     GenComment(struct_def.doc_comment, code_ptr, nullptr);
@@ -658,7 +822,13 @@ class CppGenerator : public BaseGenerator {
     }
     code += prefix + "verifier.EndTable()";
     code += ";\n  }\n";
-    code += "};\n\n";
+
+    if (parser_.opts.generate_object_based_api) {
+      // Generate the UnPack() pre declaration.
+      code += "  " + TableUnPackSignature(struct_def, true) + ";\n";
+    }
+
+    code += "};\n\n";  // End of table.
 
     // Generate a builder struct, with methods of the form:
     // void add_name(type name) { fbb_.AddElement<type>(offset, name, default);
@@ -782,6 +952,163 @@ class CppGenerator : public BaseGenerator {
       }
       code += ");\n}\n\n";
     }
+
+    if (parser_.opts.generate_object_based_api) {
+      // Generate a pre-declaration for a CreateX method that works with an
+      // unpacked C++ object.
+      code += TableCreateSignature(struct_def) + ";\n\n";
+    }
+  }
+
+  // Generate code for tables that needs to come after the regular definition.
+  void GenTablePost(StructDef &struct_def, std::string *code_ptr) {
+    std::string &code = *code_ptr;
+
+    if (parser_.opts.generate_object_based_api) {
+      // Generate the UnPack() method.
+      code += "inline " + TableUnPackSignature(struct_def, false) + " {\n";
+      code += "  auto _o = new " + NativeName(struct_def.name) + "();\n";
+      for (auto it = struct_def.fields.vec.begin();
+           it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (!field.deprecated) {
+          auto prefix = "  { auto _e = " + field.name + "(); ";
+          if (!IsScalar(field.value.type.base_type)) prefix += "if (_e) ";
+          auto deref = "_o->";
+          auto dest = deref + field.name;
+          auto assign = prefix + dest + " = ";
+          auto gen_unpack_val = [&](const Type &type, const std::string &val,
+                                    bool invector) {
+            switch (type.base_type) {
+              case BASE_TYPE_STRING:
+                return val + "->str()";
+              case BASE_TYPE_STRUCT:
+                if (IsStruct(type)) {
+                  if (invector) {
+                    return "*" + val;
+                  } else {
+                    return "std::unique_ptr<" + type.struct_def->name +
+                           ">(new " + type.struct_def->name + "(*" + val + "))";
+                  }
+                } else {
+                  return val + "->UnPack()";
+                }
+              default:
+                return val;
+                break;
+            }
+          };
+          switch (field.value.type.base_type) {
+            case BASE_TYPE_VECTOR:
+              code += prefix;
+              code += "{ for (size_t _i = 0; _i < _e->size(); _i++) { ";
+              code += dest + ".push_back(";
+              code += gen_unpack_val(field.value.type.VectorType(),
+                                     "_e->Get(_i)", true);
+              code += "); } }";
+              break;
+            case BASE_TYPE_UTYPE: {
+              auto &union_field = **(it + 1);
+              assert(union_field.value.type.base_type == BASE_TYPE_UNION);
+              code += prefix + deref + union_field.name + ".type = _e;";
+              break;
+            }
+            case BASE_TYPE_UNION:
+              code += prefix + dest + ".table = ";
+              code += field.value.type.enum_def->name;
+              code += "Union::UnPack(_e, ";
+              code += field.name + UnionTypeFieldSuffix() + "());";
+              break;
+            default:
+              code += assign + gen_unpack_val(field.value.type, "_e", false);
+              code += ";";
+              break;
+          }
+          code += " };\n";
+        }
+      }
+      code += "  return std::unique_ptr<" + NativeName(struct_def.name);
+      code += ">(_o);\n}\n\n";
+
+      // Generate a CreateX method that works with an unpacked C++ object.
+      code += TableCreateSignature(struct_def) + " {\n";
+      auto before_return_statement = code.size();
+      code += "  return Create";
+      code += struct_def.name + "(_fbb";
+      bool any_fields = false;
+      for (auto it = struct_def.fields.vec.begin();
+           it != struct_def.fields.vec.end(); ++it) {
+        auto &field = **it;
+        if (!field.deprecated) {
+          any_fields = true;
+          auto field_name = field.name;
+          if (field.value.type.base_type == BASE_TYPE_UTYPE) {
+            field_name = field_name.substr(0, field_name.size() -
+                                              strlen(UnionTypeFieldSuffix()));
+            field_name += ".type";
+          }
+          auto accessor = "_o->" + field_name;
+          auto ptrprefix = accessor + " ? ";
+          auto stlprefix = accessor + ".size() ? ";
+          auto postfix = " : 0";
+          if (field.required &&
+              (field.value.type.base_type == BASE_TYPE_STRING ||
+               field.value.type.base_type == BASE_TYPE_VECTOR)) {
+            stlprefix = "";
+            postfix = "";
+          }
+          code += ",\n    ";
+          switch (field.value.type.base_type) {
+            case BASE_TYPE_STRING:
+              code += stlprefix + "_fbb.CreateString(" + accessor + ")";
+              code += postfix;
+              break;
+            case BASE_TYPE_VECTOR: {
+              auto vector_type = field.value.type.VectorType();
+              code += stlprefix;
+              switch (vector_type.base_type) {
+                case BASE_TYPE_STRING:
+                  code += "_fbb.CreateVectorOfStrings(" + accessor + ")";
+                  break;
+                case BASE_TYPE_STRUCT:
+                  if (IsStruct(vector_type)) {
+                    code += "_fbb.CreateVectorOfStructs(" + accessor + ")";
+                  } else {
+                    code += "_fbb.CreateVector<flatbuffers::Offset<";
+                    code += vector_type.struct_def->name + ">>(" + accessor;
+                    code += ".size(), [&](size_t i) { return Create";
+                    code += vector_type.struct_def->name + "(_fbb, " + accessor;
+                    code += "[i].get()); })";
+                  }
+                  break;
+                default:
+                  code += "_fbb.CreateVector(" + accessor + ")";
+                  break;
+              }
+              code += postfix;
+              break;
+            }
+            case BASE_TYPE_UNION:
+              code += accessor + ".Pack(_fbb)";
+              break;
+            case BASE_TYPE_STRUCT:
+              if (IsStruct(field.value.type)) {
+                code += ptrprefix + accessor + ".get()" + postfix;
+              } else {
+                code += ptrprefix + "Create";
+                code += field.value.type.struct_def->name;
+                code += "(_fbb, " + accessor + ".get())" + postfix;
+              }
+              break;
+            default:
+              code += accessor;
+              break;
+          }
+        }
+      }
+      code += ");\n}\n\n";
+      if (!any_fields) code.insert(before_return_statement, "  (void)_o;\n");
+    }
   }
 
   static void GenPadding(const FieldDef &field, std::string &code,
@@ -838,6 +1165,15 @@ class CppGenerator : public BaseGenerator {
     code += "\n public:\n";
     GenFullyQualifiedNameGetter(struct_def.name, code);
 
+    // Generate a default constructor.
+    code += "  " + struct_def.name + "() { memset(this, 0, sizeof(";
+    code += struct_def.name + ")); }\n";
+
+    // Generate a copy constructor.
+    code += "  " + struct_def.name + "(const " + struct_def.name;
+    code += " &_o) { memcpy(this, &_o, sizeof(";
+    code += struct_def.name + ")); }\n";
+
     // Generate a constructor that takes all fields as arguments.
     code += "  " + struct_def.name + "(";
     for (auto it = struct_def.fields.vec.begin();
@@ -941,6 +1277,7 @@ class CppGenerator : public BaseGenerator {
     cur_name_space_ = ns;
   }
 };
+
 }  // namespace cpp
 
 bool GenerateCPP(const Parser &parser, const std::string &path,
index 53d5470..4b4a535 100644 (file)
@@ -12,6 +12,6 @@
 :: See the License for the specific language governing permissions and
 :: limitations under the License.
 
-..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json
+..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json
 ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test\namespace_test1.fbs namespace_test\namespace_test2.fbs
 ..\flatc.exe --binary --schema monster_test.fbs
index 3436d85..1b347ba 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json
+../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json
 ../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs
 ../flatc --binary --schema monster_test.fbs
 
index 9f3d4de..da27de7 100644 (file)
@@ -9,6 +9,7 @@ namespace MyGame {
 namespace Example2 {
 
 struct Monster;
+struct MonsterT;
 
 }  // namespace Example2
 
@@ -17,12 +18,15 @@ namespace Example {
 struct Test;
 
 struct TestSimpleTableWithEnum;
+struct TestSimpleTableWithEnumT;
 
 struct Vec3;
 
 struct Stat;
+struct StatT;
 
 struct Monster;
+struct MonsterT;
 
 enum Color {
   Color_Red = 1,
@@ -48,6 +52,23 @@ enum Any {
   Any_MAX = Any_MyGame_Example2_Monster
 };
 
+struct AnyUnion {
+  Any type;
+
+  flatbuffers::NativeTable *table;
+  AnyUnion() : type(Any_NONE), table(nullptr) {}
+  AnyUnion(const AnyUnion &);
+  AnyUnion &operator=(const AnyUnion &);
+  ~AnyUnion();
+
+  static flatbuffers::NativeTable *UnPack(const void *union_obj, Any type);
+  flatbuffers::Offset<void> Pack(flatbuffers::FlatBufferBuilder &_fbb) const;
+
+  MonsterT *AsMonster() { return type == Any_Monster ? reinterpret_cast<MonsterT *>(table) : nullptr; }
+  TestSimpleTableWithEnumT *AsTestSimpleTableWithEnum() { return type == Any_TestSimpleTableWithEnum ? reinterpret_cast<TestSimpleTableWithEnumT *>(table) : nullptr; }
+  MyGame::Example2::MonsterT *AsMyGame_Example2_Monster() { return type == Any_MyGame_Example2_Monster ? reinterpret_cast<MyGame::Example2::MonsterT *>(table) : nullptr; }
+};
+
 inline const char **EnumNamesAny() {
   static const char *names[] = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", nullptr };
   return names;
@@ -64,6 +85,8 @@ MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS {
   int8_t __padding0;
 
  public:
+  Test() { memset(this, 0, sizeof(Test)); }
+  Test(const Test &_o) { memcpy(this, &_o, sizeof(Test)); }
   Test(int16_t _a, int8_t _b)
     : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)), __padding0(0) { (void)__padding0; }
 
@@ -87,6 +110,8 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 FLATBUFFERS_FINAL_CLASS {
   int16_t __padding2;
 
  public:
+  Vec3() { memset(this, 0, sizeof(Vec3)); }
+  Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); }
   Vec3(float _x, float _y, float _z, double _test1, Color _test2, const Test &_test3)
     : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)), __padding0(0), test1_(flatbuffers::EndianScalar(_test1)), test2_(flatbuffers::EndianScalar(static_cast<int8_t>(_test2))), __padding1(0), test3_(_test3), __padding2(0) { (void)__padding0; (void)__padding1; (void)__padding2; }
 
@@ -109,11 +134,15 @@ STRUCT_END(Vec3, 32);
 
 namespace Example2 {
 
+struct MonsterT : public flatbuffers::NativeTable {
+};
+
 struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   bool Verify(flatbuffers::Verifier &verifier) const {
     return VerifyTableStart(verifier) &&
            verifier.EndTable();
   }
+  std::unique_ptr<MonsterT> UnPack() const;
 };
 
 struct MonsterBuilder {
@@ -132,10 +161,16 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
   return builder_.Finish();
 }
 
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o);
+
 }  // namespace Example2
 
 namespace Example {
 
+struct TestSimpleTableWithEnumT : public flatbuffers::NativeTable {
+  Color color;
+};
+
 struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum {
     VT_COLOR = 4
@@ -147,6 +182,7 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta
            VerifyField<int8_t>(verifier, VT_COLOR) &&
            verifier.EndTable();
   }
+  std::unique_ptr<TestSimpleTableWithEnumT> UnPack() const;
 };
 
 struct TestSimpleTableWithEnumBuilder {
@@ -168,6 +204,14 @@ inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnu
   return builder_.Finish();
 }
 
+inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o);
+
+struct StatT : public flatbuffers::NativeTable {
+  std::string id;
+  int64_t val;
+  uint16_t count;
+};
+
 struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum {
     VT_ID = 4,
@@ -188,6 +232,7 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
            VerifyField<uint16_t>(verifier, VT_COUNT) &&
            verifier.EndTable();
   }
+  std::unique_ptr<StatT> UnPack() const;
 };
 
 struct StatBuilder {
@@ -222,6 +267,38 @@ inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb
   return CreateStat(_fbb, id ? 0 : _fbb.CreateString(id), val, count);
 }
 
+inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o);
+
+struct MonsterT : public flatbuffers::NativeTable {
+  std::unique_ptr<Vec3> pos;
+  int16_t mana;
+  int16_t hp;
+  std::string name;
+  std::vector<uint8_t> inventory;
+  Color color;
+  AnyUnion test;
+  std::vector<Test> test4;
+  std::vector<std::string> testarrayofstring;
+  std::vector<std::unique_ptr<MonsterT>> testarrayoftables;
+  std::unique_ptr<MonsterT> enemy;
+  std::vector<uint8_t> testnestedflatbuffer;
+  std::unique_ptr<StatT> testempty;
+  bool testbool;
+  int32_t testhashs32_fnv1;
+  uint32_t testhashu32_fnv1;
+  int64_t testhashs64_fnv1;
+  uint64_t testhashu64_fnv1;
+  int32_t testhashs32_fnv1a;
+  uint32_t testhashu32_fnv1a;
+  int64_t testhashs64_fnv1a;
+  uint64_t testhashu64_fnv1a;
+  std::vector<bool> testarrayofbools;
+  float testf;
+  float testf2;
+  float testf3;
+  std::vector<std::string> testarrayofstring2;
+};
+
 /// an example documentation comment: monster object
 struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   enum {
@@ -361,6 +438,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
            verifier.VerifyVectorOfStrings(testarrayofstring2()) &&
            verifier.EndTable();
   }
+  std::unique_ptr<MonsterT> UnPack() const;
 };
 
 struct MonsterBuilder {
@@ -496,6 +574,117 @@ inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder
   return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector<uint8_t>(*inventory), color, test_type, test, test4 ? 0 : _fbb.CreateVector<const Test *>(*test4), testarrayofstring ? 0 : _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*testarrayofstring), testarrayoftables ? 0 : _fbb.CreateVector<flatbuffers::Offset<Monster>>(*testarrayoftables), enemy, testnestedflatbuffer ? 0 : _fbb.CreateVector<uint8_t>(*testnestedflatbuffer), testempty, testbool, testhashs32_fnv1, testhashu32_fnv1, testhashs64_fnv1, testhashu64_fnv1, testhashs32_fnv1a, testhashu32_fnv1a, testhashs64_fnv1a, testhashu64_fnv1a, testarrayofbools ? 0 : _fbb.CreateVector<uint8_t>(*testarrayofbools), testf, testf2, testf3, testarrayofstring2 ? 0 : _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*testarrayofstring2));
 }
 
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o);
+
+}  // namespace Example
+
+namespace Example2 {
+
+inline std::unique_ptr<MonsterT> Monster::UnPack() const {
+  auto _o = new MonsterT();
+  return std::unique_ptr<MonsterT>(_o);
+}
+
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) {
+  (void)_o;
+  return CreateMonster(_fbb);
+}
+
+}  // namespace Example2
+
+namespace Example {
+
+inline std::unique_ptr<TestSimpleTableWithEnumT> TestSimpleTableWithEnum::UnPack() const {
+  auto _o = new TestSimpleTableWithEnumT();
+  { auto _e = color(); _o->color = _e; };
+  return std::unique_ptr<TestSimpleTableWithEnumT>(_o);
+}
+
+inline flatbuffers::Offset<TestSimpleTableWithEnum> CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o) {
+  return CreateTestSimpleTableWithEnum(_fbb,
+    _o->color);
+}
+
+inline std::unique_ptr<StatT> Stat::UnPack() const {
+  auto _o = new StatT();
+  { auto _e = id(); if (_e) _o->id = _e->str(); };
+  { auto _e = val(); _o->val = _e; };
+  { auto _e = count(); _o->count = _e; };
+  return std::unique_ptr<StatT>(_o);
+}
+
+inline flatbuffers::Offset<Stat> CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o) {
+  return CreateStat(_fbb,
+    _o->id.size() ? _fbb.CreateString(_o->id) : 0,
+    _o->val,
+    _o->count);
+}
+
+inline std::unique_ptr<MonsterT> Monster::UnPack() const {
+  auto _o = new MonsterT();
+  { auto _e = pos(); if (_e) _o->pos = std::unique_ptr<Vec3>(new Vec3(*_e)); };
+  { auto _e = mana(); _o->mana = _e; };
+  { auto _e = hp(); _o->hp = _e; };
+  { auto _e = name(); if (_e) _o->name = _e->str(); };
+  { auto _e = inventory(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } };
+  { auto _e = color(); _o->color = _e; };
+  { auto _e = test_type(); _o->test.type = _e; };
+  { auto _e = test(); if (_e) _o->test.table = AnyUnion::UnPack(_e, test_type()); };
+  { auto _e = test4(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->test4.push_back(*_e->Get(_i)); } } };
+  { auto _e = testarrayofstring(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring.push_back(_e->Get(_i)->str()); } } };
+  { auto _e = testarrayoftables(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->testarrayoftables.push_back(_e->Get(_i)->UnPack()); } } };
+  { auto _e = enemy(); if (_e) _o->enemy = _e->UnPack(); };
+  { auto _e = testnestedflatbuffer(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->testnestedflatbuffer.push_back(_e->Get(_i)); } } };
+  { auto _e = testempty(); if (_e) _o->testempty = _e->UnPack(); };
+  { auto _e = testbool(); _o->testbool = _e; };
+  { auto _e = testhashs32_fnv1(); _o->testhashs32_fnv1 = _e; };
+  { auto _e = testhashu32_fnv1(); _o->testhashu32_fnv1 = _e; };
+  { auto _e = testhashs64_fnv1(); _o->testhashs64_fnv1 = _e; };
+  { auto _e = testhashu64_fnv1(); _o->testhashu64_fnv1 = _e; };
+  { auto _e = testhashs32_fnv1a(); _o->testhashs32_fnv1a = _e; };
+  { auto _e = testhashu32_fnv1a(); _o->testhashu32_fnv1a = _e; };
+  { auto _e = testhashs64_fnv1a(); _o->testhashs64_fnv1a = _e; };
+  { auto _e = testhashu64_fnv1a(); _o->testhashu64_fnv1a = _e; };
+  { auto _e = testarrayofbools(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofbools.push_back(_e->Get(_i)); } } };
+  { auto _e = testf(); _o->testf = _e; };
+  { auto _e = testf2(); _o->testf2 = _e; };
+  { auto _e = testf3(); _o->testf3 = _e; };
+  { auto _e = testarrayofstring2(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring2.push_back(_e->Get(_i)->str()); } } };
+  return std::unique_ptr<MonsterT>(_o);
+}
+
+inline flatbuffers::Offset<Monster> CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) {
+  return CreateMonster(_fbb,
+    _o->pos ? _o->pos.get() : 0,
+    _o->mana,
+    _o->hp,
+    _fbb.CreateString(_o->name),
+    _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0,
+    _o->color,
+    _o->test.type,
+    _o->test.Pack(_fbb),
+    _o->test4.size() ? _fbb.CreateVectorOfStructs(_o->test4) : 0,
+    _o->testarrayofstring.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring) : 0,
+    _o->testarrayoftables.size() ? _fbb.CreateVector<flatbuffers::Offset<Monster>>(_o->testarrayoftables.size(), [&](size_t i) { return CreateMonster(_fbb, _o->testarrayoftables[i].get()); }) : 0,
+    _o->enemy ? CreateMonster(_fbb, _o->enemy.get()) : 0,
+    _o->testnestedflatbuffer.size() ? _fbb.CreateVector(_o->testnestedflatbuffer) : 0,
+    _o->testempty ? CreateStat(_fbb, _o->testempty.get()) : 0,
+    _o->testbool,
+    _o->testhashs32_fnv1,
+    _o->testhashu32_fnv1,
+    _o->testhashs64_fnv1,
+    _o->testhashu64_fnv1,
+    _o->testhashs32_fnv1a,
+    _o->testhashu32_fnv1a,
+    _o->testhashs64_fnv1a,
+    _o->testhashu64_fnv1a,
+    _o->testarrayofbools.size() ? _fbb.CreateVector(_o->testarrayofbools) : 0,
+    _o->testf,
+    _o->testf2,
+    _o->testf3,
+    _o->testarrayofstring2.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring2) : 0);
+}
+
 inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, Any type) {
   switch (type) {
     case Any_NONE: return true;
@@ -506,6 +695,35 @@ inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, An
   }
 }
 
+inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *union_obj, Any type) {
+  switch (type) {
+    case Any_NONE: return nullptr;
+    case Any_Monster: return reinterpret_cast<const Monster *>(union_obj)->UnPack().release();
+    case Any_TestSimpleTableWithEnum: return reinterpret_cast<const TestSimpleTableWithEnum *>(union_obj)->UnPack().release();
+    case Any_MyGame_Example2_Monster: return reinterpret_cast<const MyGame::Example2::Monster *>(union_obj)->UnPack().release();
+    default: return nullptr;
+  }
+}
+
+inline flatbuffers::Offset<void> AnyUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const {
+  switch (type) {
+    case Any_NONE: return 0;
+    case Any_Monster: return CreateMonster(_fbb, reinterpret_cast<const MonsterT *>(table)).Union();
+    case Any_TestSimpleTableWithEnum: return CreateTestSimpleTableWithEnum(_fbb, reinterpret_cast<const TestSimpleTableWithEnumT *>(table)).Union();
+    case Any_MyGame_Example2_Monster: return CreateMonster(_fbb, reinterpret_cast<const MyGame::Example2::MonsterT *>(table)).Union();
+    default: return 0;
+  }
+}
+
+inline AnyUnion::~AnyUnion() {
+  switch (type) {
+    case Any_Monster: delete reinterpret_cast<MonsterT *>(table); break;
+    case Any_TestSimpleTableWithEnum: delete reinterpret_cast<TestSimpleTableWithEnumT *>(table); break;
+    case Any_MyGame_Example2_Monster: delete reinterpret_cast<MyGame::Example2::MonsterT *>(table); break;
+    default:;
+  }
+}
+
 inline const MyGame::Example::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot<MyGame::Example::Monster>(buf); }
 
 inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot<Monster>(buf); }
index 23a0964..59d4030 100644 (file)
@@ -33,6 +33,8 @@ MANUALLY_ALIGNED_STRUCT(4) StructInNestedNS FLATBUFFERS_FINAL_CLASS {
   int32_t b_;
 
  public:
+  StructInNestedNS() { memset(this, 0, sizeof(StructInNestedNS)); }
+  StructInNestedNS(const StructInNestedNS &_o) { memcpy(this, &_o, sizeof(StructInNestedNS)); }
   StructInNestedNS(int32_t _a, int32_t _b)
     : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)) { }
 
index 885eae2..77578bc 100644 (file)
@@ -154,4 +154,12 @@ inline flatbuffers::Offset<SecondTableInA> CreateSecondTableInA(flatbuffers::Fla
 
 }  // namespace NamespaceA
 
+namespace NamespaceC {
+
+}  // namespace NamespaceC
+
+namespace NamespaceA {
+
+}  // namespace NamespaceA
+
 #endif  // FLATBUFFERS_GENERATED_NAMESPACETEST2_NAMESPACEA_H_
index 402843a..bcd54f5 100644 (file)
@@ -155,7 +155,8 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) {
 }
 
 //  example of accessing a buffer loaded in memory:
-void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) {
+void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length,
+                          bool pooled = true) {
 
   // First, verify the buffers integrity (optional)
   flatbuffers::Verifier verifier(flatbuf, length);
@@ -218,9 +219,11 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) {
   TEST_EQ(vecofstrings->Length(), 4U);
   TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob");
   TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred");
-  // These should have pointer equality because of string pooling.
-  TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
-  TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
+  if (pooled) {
+    // These should have pointer equality because of string pooling.
+    TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str());
+    TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str());
+  }
 
   auto vecofstrings2 = monster->testarrayofstring2();
   if (vecofstrings2) {
@@ -305,6 +308,77 @@ void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
   AccessFlatBufferTest(flatbuf, length);
 }
 
+// Unpack a FlatBuffer into objects.
+void ObjectFlatBuffersTest(uint8_t *flatbuf, std::size_t length) {
+  // Turn a buffer into C++ objects.
+  auto monster1 = GetMonster(flatbuf)->UnPack();
+
+  // Re-serialize the data.
+  flatbuffers::FlatBufferBuilder fbb1;
+  fbb1.Finish(CreateMonster(fbb1, monster1.get()), MonsterIdentifier());
+
+  // Unpack again, and re-serialize again.
+  auto monster2 = GetMonster(fbb1.GetBufferPointer())->UnPack();
+  flatbuffers::FlatBufferBuilder fbb2;
+  fbb2.Finish(CreateMonster(fbb2, monster2.get()), MonsterIdentifier());
+
+  // Now we've gone full round-trip, the two buffers should match.
+  auto len1 = fbb1.GetSize();
+  auto len2 = fbb2.GetSize();
+  TEST_EQ(len1, len2);
+  TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(),
+                 len1), 0);
+
+  // Test it with the original buffer test to make sure all data survived.
+  AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false);
+
+  // Test accessing fields, similar to AccessFlatBufferTest above.
+  TEST_EQ(monster2->hp, 80);
+  TEST_EQ(monster2->mana, 150);  // default
+  TEST_EQ_STR(monster2->name.c_str(), "MyMonster");
+
+  auto &pos = monster2->pos;
+  TEST_NOTNULL(pos);
+  TEST_EQ(pos->z(), 3);
+  TEST_EQ(pos->test3().a(), 10);
+  TEST_EQ(pos->test3().b(), 20);
+
+  auto &inventory = monster2->inventory;
+  TEST_EQ(inventory.size(), 10UL);
+  unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+  for (auto it = inventory.begin(); it != inventory.end(); ++it)
+    TEST_EQ(*it, inv_data[it - inventory.begin()]);
+
+  TEST_EQ(monster2->color, Color_Blue);
+
+  auto monster3 = monster2->test.AsMonster();
+  TEST_NOTNULL(monster3);
+  TEST_EQ_STR(monster3->name.c_str(), "Fred");
+
+  auto &vecofstrings = monster2->testarrayofstring;
+  TEST_EQ(vecofstrings.size(), 4U);
+  TEST_EQ_STR(vecofstrings[0].c_str(), "bob");
+  TEST_EQ_STR(vecofstrings[1].c_str(), "fred");
+
+  auto &vecofstrings2 = monster2->testarrayofstring2;
+  TEST_EQ(vecofstrings2.size(), 2U);
+  TEST_EQ_STR(vecofstrings2[0].c_str(), "jane");
+  TEST_EQ_STR(vecofstrings2[1].c_str(), "mary");
+
+  auto &vecoftables = monster2->testarrayoftables;
+  TEST_EQ(vecoftables.size(), 3U);
+  TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney");
+  TEST_EQ(vecoftables[0]->hp, 1000);
+  TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred");
+  TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma");
+
+  auto &tests = monster2->test4;
+  TEST_EQ(tests[0].a(), 10);
+  TEST_EQ(tests[0].b(), 20);
+  TEST_EQ(tests[1].a(), 30);
+  TEST_EQ(tests[1].b(), 40);
+}
+
 // example of parsing text straight into a buffer, and generating
 // text back from it:
 void ParseAndGenerateTextTest() {
@@ -855,7 +929,7 @@ void ValueTest() {
 
   // Test conversion functions.
   TEST_EQ(FloatCompare(TestValue<float>("{ Y:cos(rad(180)) }","float"), -1), true);
-  
+
   // Test negative hex constant.
   TEST_EQ(TestValue<int>("{ Y:-0x80 }","int") == -128, true);
 }
@@ -993,6 +1067,8 @@ int main(int /*argc*/, const char * /*argv*/[]) {
 
   MutateFlatBuffersTest(flatbuf.get(), rawbuf.length());
 
+  ObjectFlatBuffersTest(flatbuf.get(), rawbuf.length());
+
   #ifndef FLATBUFFERS_NO_FILE_TESTS
   ParseAndGenerateTextTest();
   ReflectionTest(flatbuf.get(), rawbuf.length());