diff --git a/.gitignore b/.gitignore index 6f3894d064bf2d660b255260d34e55d66d67fcb5..a51ef478d25ec6f5ffd5264a516ba2b87cf9fedf 100755 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ flatsamplebinary flatsamplebinary.exe flatsampletext flatsampletext.exe +grpctest +grpctest.exe snapshot.sh tests/go_gen tests/monsterdata_java_wire.mon diff --git a/CMakeLists.txt b/CMakeLists.txt index c2dd950850efeccd8e5cb9efb15592f05b0f9f46..cfb84bae4a03d117e1a6ff1fd4f6199ff80be5c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index ad584c73915e05fdbf51edb5a968cad216eab49a..1ff2c8d4e06e53c66406cd709ee3a260e2e6af2c 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -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 diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md index d4abe66e4b765dbcd2167b6c360508df5eb67546..060432b01bccc2d34272c243617d4a9340ff3cb5 100755 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -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 diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md index 6f6ac9a51c1d1e8bc149329bb4e44602031021fd..c457742a200d257badb4160c2f02756d3ad7d72b 100644 --- a/docs/source/Tutorial.md +++ b/docs/source/Tutorial.md @@ -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 diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 01ab6db042523aa7ed0bd37fc9c90618131915e0..a764d8640a0183002cfa0e92348ef5dcdc47f7c6 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -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, diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index dddfab25cd7ff73384ed0648960e9c9cb9bf2f24..a3027a0a911466f0a81462427712d6719aa3d9f5 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -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) {} }; diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 62a23ad82022fa057e510cd190bd243e86a77c8a..636aa3771b2c9a0e89727d2cac2b5026fdfa959d 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -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); } diff --git a/src/flatc.cpp b/src/flatc.cpp index d410718e3c99ba691f34d47c7ea5a4a2b43bb349..631eaf1afff0008f197a6dad1f07119134e7f2f6 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -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; diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index e12e8a34a4dfdea749fd1aca1a75a095f31bd9bf..1479ad9fb2e6487251b3b0749b2f67b347d68df6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -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, diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 53d54707bd58422dd5b3a87664f6c7cd8aec0a77..4b4a535316a1ab1a08971827a9bf40e8ea322173 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -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 diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 3436d858648692b3a38fc1ab12e23ffc0b94b037..1b347ba7943d92e0a530eb70ece54e7a5f255caf 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -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 diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 9f3d4de0fe4f4f069571cfbad56b8d6dce4ac256..da27de74da1bd5e9df389b560798c18adb383a69 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -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); } diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 23a0964fb5a216677664f76adfa27ad2c687cd93..59d4030a17c8985ce7f93645857d4c3d0ce28fa5 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -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)) { } diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 885eae29b88e139d5e73bdbf89ab22bd335dfc06..77578bc6093e46136309eb8ca23e2353f9f1f128 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -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_ diff --git a/tests/test.cpp b/tests/test.cpp index 402843a6f9ee8d7ddae282f293aad95f57a7d9db..bcd54f56426bf174353654932e47c26af5009033 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -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());