diff --git a/samples/monster_generated.h b/samples/monster_generated.h index b3fc655b0..13b8c3ea7 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -239,6 +239,10 @@ struct MonsterT : public flatbuffers::NativeTable { std::vector> weapons{}; MyGame::Sample::EquipmentUnion equipped{}; std::vector path{}; + MonsterT() = default; + MonsterT(const MonsterT &o); + MonsterT(MonsterT&&) FLATBUFFERS_NOEXCEPT = default; + MonsterT &operator=(MonsterT o) FLATBUFFERS_NOEXCEPT; }; struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -555,6 +559,32 @@ inline bool operator!=(const MonsterT &lhs, const MonsterT &rhs) { } +inline MonsterT::MonsterT(const MonsterT &o) + : pos((o.pos) ? new MyGame::Sample::Vec3(*o.pos) : nullptr), + mana(o.mana), + hp(o.hp), + name(o.name), + inventory(o.inventory), + color(o.color), + equipped(o.equipped), + path(o.path) { + weapons.reserve(o.weapons.size()); + for (const auto &v : o.weapons) { weapons.emplace_back((v) ? new MyGame::Sample::WeaponT(*v) : nullptr); } +} + +inline MonsterT &MonsterT::operator=(MonsterT o) FLATBUFFERS_NOEXCEPT { + std::swap(pos, o.pos); + std::swap(mana, o.mana); + std::swap(hp, o.hp); + std::swap(name, o.name); + std::swap(inventory, o.inventory); + std::swap(color, o.color); + std::swap(weapons, o.weapons); + std::swap(equipped, o.equipped); + std::swap(path, o.path); + return *this; +} + inline MonsterT *Monster::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new MonsterT()); UnPackTo(_o.get(), _resolver); diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index a14ac77ad..e1386fdf6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -810,7 +810,7 @@ class CppGenerator : public BaseGenerator { } std::string GenTypeNative(const Type &type, bool invector, - const FieldDef &field) { + const FieldDef &field, bool forcopy = false) { switch (type.base_type) { case BASE_TYPE_STRING: { return NativeString(&field); @@ -831,15 +831,14 @@ class CppGenerator : public BaseGenerator { if (IsStruct(type)) { auto native_type = type.struct_def->attributes.Lookup("native_type"); if (native_type) { type_name = native_type->constant; } - if (invector || field.native_inline) { + if (invector || field.native_inline || forcopy) { return type_name; } else { return GenTypeNativePtr(type_name, &field, false); } } else { - return GenTypeNativePtr( - WrapNativeNameInNameSpace(*type.struct_def, opts_), &field, - false); + const auto nn = WrapNativeNameInNameSpace(*type.struct_def, opts_); + return forcopy ? nn : GenTypeNativePtr(nn, &field, false); } } case BASE_TYPE_UNION: { @@ -1601,7 +1600,8 @@ class CppGenerator : public BaseGenerator { code_.SetValue("TYPE", GetUnionElement(ev, true, opts_)); code_ += " case {{LABEL}}: {"; bool copyable = true; - if (ev.union_type.base_type == BASE_TYPE_STRUCT && + if (opts_.g_cpp_std < cpp::CPP_STD_11 && + ev.union_type.base_type == BASE_TYPE_STRUCT && !ev.union_type.struct_def->fixed) { // Don't generate code to copy if table is not copyable. // TODO(wvo): make tables copyable instead. @@ -1804,13 +1804,50 @@ class CppGenerator : public BaseGenerator { } } + // Returns true if `struct_def` needs a copy constructor and assignment + // operator because it has one or more table members, struct members with a + // custom cpp_type and non-naked pointer type, or vector members of those. + bool NeedsCopyCtorAssignOp(const StructDef &struct_def) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + const auto &type = field.value.type; + if (field.deprecated) continue; + if (type.base_type == BASE_TYPE_STRUCT) { + const auto cpp_type = field.attributes.Lookup("cpp_type"); + const auto cpp_ptr_type = field.attributes.Lookup("cpp_ptr_type"); + const bool is_ptr = !(IsStruct(type) && field.native_inline) || + (cpp_type && cpp_ptr_type->constant != "naked"); + if (is_ptr) { return true; } + } else if (IsVector(type)) { + const auto vec_type = type.VectorType(); + if (vec_type.base_type == BASE_TYPE_UTYPE) continue; + const auto cpp_type = field.attributes.Lookup("cpp_type"); + const auto cpp_ptr_type = field.attributes.Lookup("cpp_ptr_type"); + const bool is_ptr = + (vec_type.base_type == BASE_TYPE_STRUCT && !IsStruct(vec_type)) || + (cpp_type && cpp_ptr_type->constant != "naked"); + if (is_ptr) { return true; } + } + } + return false; + } + // Generate the default constructor for this struct. Properly initialize all // scalar members with default values. void GenDefaultConstructor(const StructDef &struct_def) { code_.SetValue("NATIVE_NAME", NativeName(Name(struct_def), &struct_def, opts_)); - // In >= C++11, default member initializers are generated. - if (opts_.g_cpp_std >= cpp::CPP_STD_11) { return; } + // In >= C++11, default member initializers are generated. To allow for + // aggregate initialization, do not emit a default constructor at all, with + // the exception of types that need a copy/move ctors and assignment + // operators. + if (opts_.g_cpp_std >= cpp::CPP_STD_11) { + if (NeedsCopyCtorAssignOp(struct_def)) { + code_ += " {{NATIVE_NAME}}() = default;"; + } + return; + } std::string initializer_list; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -1854,6 +1891,125 @@ class CppGenerator : public BaseGenerator { code_ += " }"; } + // Generate the >= C++11 copy/move constructor and assignment operator + // declarations if required. Tables that are default-copyable do not get + // user-provided copy/move constructors and assignment operators so they + // remain aggregates. + void GenCopyMoveCtorAndAssigOpDecls(const StructDef &struct_def) { + if (opts_.g_cpp_std < cpp::CPP_STD_11) return; + if (!NeedsCopyCtorAssignOp(struct_def)) return; + code_.SetValue("NATIVE_NAME", + NativeName(Name(struct_def), &struct_def, opts_)); + code_ += " {{NATIVE_NAME}}(const {{NATIVE_NAME}} &o);"; + code_ += + " {{NATIVE_NAME}}({{NATIVE_NAME}}&&) FLATBUFFERS_NOEXCEPT = " + "default;"; + code_ += + " {{NATIVE_NAME}} &operator=({{NATIVE_NAME}} o) FLATBUFFERS_NOEXCEPT;"; + } + + // Generate the >= C++11 copy constructor and assignment operator definitions. + void GenCopyCtorAssignOpDefs(const StructDef &struct_def) { + if (opts_.g_cpp_std < cpp::CPP_STD_11) return; + if (!NeedsCopyCtorAssignOp(struct_def)) return; + std::string initializer_list; + std::string vector_copies; + std::string swaps; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + const auto &type = field.value.type; + if (field.deprecated || type.base_type == BASE_TYPE_UTYPE) continue; + if (type.base_type == BASE_TYPE_STRUCT) { + if (!initializer_list.empty()) { initializer_list += ",\n "; } + const auto cpp_type = field.attributes.Lookup("cpp_type"); + const auto cpp_ptr_type = field.attributes.Lookup("cpp_ptr_type"); + auto type_name = (cpp_type) ? cpp_type->constant + : GenTypeNative(type, /*invector*/ false, + field, /*forcopy*/ true); + const bool is_ptr = !(IsStruct(type) && field.native_inline) || + (cpp_type && cpp_ptr_type->constant != "naked"); + CodeWriter cw; + cw.SetValue("FIELD", Name(field)); + cw.SetValue("TYPE", type_name); + if (is_ptr) { + cw += + "{{FIELD}}((o.{{FIELD}}) ? new {{TYPE}}(*o.{{FIELD}}) : " + "nullptr)\\"; + initializer_list += cw.ToString(); + } else { + cw += "{{FIELD}}(o.{{FIELD}})\\"; + initializer_list += cw.ToString(); + } + } else if (IsVector(type)) { + const auto vec_type = type.VectorType(); + if (vec_type.base_type == BASE_TYPE_UTYPE) continue; + const auto cpp_type = field.attributes.Lookup("cpp_type"); + const auto cpp_ptr_type = field.attributes.Lookup("cpp_ptr_type"); + const auto type_name = (cpp_type) + ? cpp_type->constant + : GenTypeNative(vec_type, /*invector*/ true, + field, /*forcopy*/ true); + const bool is_ptr = + (vec_type.base_type == BASE_TYPE_STRUCT && !IsStruct(vec_type)) || + (cpp_type && cpp_ptr_type->constant != "naked"); + CodeWriter cw(" "); + cw.SetValue("FIELD", Name(field)); + cw.SetValue("TYPE", type_name); + if (is_ptr) { + // Use emplace_back to construct the potentially-smart pointer element + // from a raw pointer to a new-allocated copy. + cw.IncrementIdentLevel(); + cw += "{{FIELD}}.reserve(o.{{FIELD}}.size());"; + cw += + "for (const auto &v : o.{{FIELD}}) { " + "{{FIELD}}.emplace_back((v) ? new {{TYPE}}(*v) : nullptr); }"; + vector_copies += cw.ToString(); + } else { + // For non-pointer elements, use std::vector's copy constructor in the + // initializer list. This will yield better performance than an insert + // range loop for trivially-copyable element types. + if (!initializer_list.empty()) { initializer_list += ",\n "; } + cw += "{{FIELD}}(o.{{FIELD}})\\"; + initializer_list += cw.ToString(); + } + } else { + if (!initializer_list.empty()) { initializer_list += ",\n "; } + CodeWriter cw; + cw.SetValue("FIELD", Name(field)); + cw += "{{FIELD}}(o.{{FIELD}})\\"; + initializer_list += cw.ToString(); + } + { + if (!swaps.empty()) { swaps += "\n "; } + CodeWriter cw; + cw.SetValue("FIELD", Name(field)); + cw += "std::swap({{FIELD}}, o.{{FIELD}});\\"; + swaps += cw.ToString(); + } + } + if (!initializer_list.empty()) { + initializer_list = "\n : " + initializer_list; + } + if (!swaps.empty()) { swaps = " " + swaps; } + + code_.SetValue("NATIVE_NAME", + NativeName(Name(struct_def), &struct_def, opts_)); + code_.SetValue("INIT_LIST", initializer_list); + code_.SetValue("VEC_COPY", vector_copies); + code_.SetValue("SWAPS", swaps); + + code_ += + "inline {{NATIVE_NAME}}::{{NATIVE_NAME}}(const {{NATIVE_NAME}} &o)" + "{{INIT_LIST}} {"; + code_ += "{{VEC_COPY}}}\n"; + code_ += + "inline {{NATIVE_NAME}} &{{NATIVE_NAME}}::operator=" + "({{NATIVE_NAME}} o) FLATBUFFERS_NOEXCEPT {"; + code_ += "{{SWAPS}}"; + code_ += " return *this;\n}\n"; + } + void GenCompareOperator(const StructDef &struct_def, std::string accessSuffix = "") { std::string compare_op; @@ -1942,6 +2098,7 @@ class CppGenerator : public BaseGenerator { } GenOperatorNewDelete(struct_def); GenDefaultConstructor(struct_def); + GenCopyMoveCtorAndAssigOpDecls(struct_def); code_ += "};"; code_ += ""; } @@ -3120,6 +3277,9 @@ class CppGenerator : public BaseGenerator { NativeName(Name(struct_def), &struct_def, opts_)); if (opts_.generate_object_based_api) { + // Generate the >= C++11 copy ctor and assignment operator definitions. + GenCopyCtorAssignOpDefs(struct_def); + // Generate the X::UnPack() method. code_ += "inline " + TableUnPackSignature(struct_def, false, opts_) + " {"; diff --git a/tests/arrays_test_generated.h b/tests/arrays_test_generated.h index 007a5f5b9..1300b9272 100644 --- a/tests/arrays_test_generated.h +++ b/tests/arrays_test_generated.h @@ -265,6 +265,10 @@ inline bool operator!=(const ArrayStruct &lhs, const ArrayStruct &rhs) { struct ArrayTableT : public flatbuffers::NativeTable { typedef ArrayTable TableType; flatbuffers::unique_ptr a{}; + ArrayTableT() = default; + ArrayTableT(const ArrayTableT &o); + ArrayTableT(ArrayTableT&&) FLATBUFFERS_NOEXCEPT = default; + ArrayTableT &operator=(ArrayTableT o) FLATBUFFERS_NOEXCEPT; }; struct ArrayTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -331,6 +335,15 @@ inline bool operator!=(const ArrayTableT &lhs, const ArrayTableT &rhs) { } +inline ArrayTableT::ArrayTableT(const ArrayTableT &o) + : a((o.a) ? new MyGame::Example::ArrayStruct(*o.a) : nullptr) { +} + +inline ArrayTableT &ArrayTableT::operator=(ArrayTableT o) FLATBUFFERS_NOEXCEPT { + std::swap(a, o.a); + return *this; +} + inline ArrayTableT *ArrayTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new ArrayTableT()); UnPackTo(_o.get(), _resolver); diff --git a/tests/cpp17/generated_cpp17/monster_test_generated.h b/tests/cpp17/generated_cpp17/monster_test_generated.h index ad09dacc1..f201c85db 100644 --- a/tests/cpp17/generated_cpp17/monster_test_generated.h +++ b/tests/cpp17/generated_cpp17/monster_test_generated.h @@ -1238,6 +1238,10 @@ struct MonsterT : public flatbuffers::NativeTable { MyGame::Example::Race signed_enum = MyGame::Example::Race::None; std::vector testrequirednestedflatbuffer{}; std::vector> scalar_key_sorted_tables{}; + MonsterT() = default; + MonsterT(const MonsterT &o); + MonsterT(MonsterT&&) FLATBUFFERS_NOEXCEPT = default; + MonsterT &operator=(MonsterT o) FLATBUFFERS_NOEXCEPT; }; /// an example documentation comment: "monster object" @@ -2698,6 +2702,112 @@ inline flatbuffers::Offset CreateReferrable(flatbuffers::FlatBufferB _id); } +inline MonsterT::MonsterT(const MonsterT &o) + : pos((o.pos) ? new MyGame::Example::Vec3(*o.pos) : nullptr), + mana(o.mana), + hp(o.hp), + name(o.name), + inventory(o.inventory), + color(o.color), + test(o.test), + test4(o.test4), + testarrayofstring(o.testarrayofstring), + enemy((o.enemy) ? new MyGame::Example::MonsterT(*o.enemy) : nullptr), + testnestedflatbuffer(o.testnestedflatbuffer), + testempty((o.testempty) ? new MyGame::Example::StatT(*o.testempty) : nullptr), + testbool(o.testbool), + testhashs32_fnv1(o.testhashs32_fnv1), + testhashu32_fnv1(o.testhashu32_fnv1), + testhashs64_fnv1(o.testhashs64_fnv1), + testhashu64_fnv1(o.testhashu64_fnv1), + testhashs32_fnv1a(o.testhashs32_fnv1a), + testhashu32_fnv1a(o.testhashu32_fnv1a), + testhashs64_fnv1a(o.testhashs64_fnv1a), + testhashu64_fnv1a(o.testhashu64_fnv1a), + testarrayofbools(o.testarrayofbools), + testf(o.testf), + testf2(o.testf2), + testf3(o.testf3), + testarrayofstring2(o.testarrayofstring2), + testarrayofsortedstruct(o.testarrayofsortedstruct), + flex(o.flex), + test5(o.test5), + vector_of_longs(o.vector_of_longs), + vector_of_doubles(o.vector_of_doubles), + parent_namespace_test((o.parent_namespace_test) ? new MyGame::InParentNamespaceT(*o.parent_namespace_test) : nullptr), + single_weak_reference(o.single_weak_reference), + vector_of_weak_references(o.vector_of_weak_references), + co_owning_reference(o.co_owning_reference), + non_owning_reference(o.non_owning_reference), + vector_of_non_owning_references(o.vector_of_non_owning_references), + any_unique(o.any_unique), + any_ambiguous(o.any_ambiguous), + vector_of_enums(o.vector_of_enums), + signed_enum(o.signed_enum), + testrequirednestedflatbuffer(o.testrequirednestedflatbuffer) { + testarrayoftables.reserve(o.testarrayoftables.size()); + for (const auto &v : o.testarrayoftables) { testarrayoftables.emplace_back((v) ? new MyGame::Example::MonsterT(*v) : nullptr); } + vector_of_referrables.reserve(o.vector_of_referrables.size()); + for (const auto &v : o.vector_of_referrables) { vector_of_referrables.emplace_back((v) ? new MyGame::Example::ReferrableT(*v) : nullptr); } + vector_of_strong_referrables.reserve(o.vector_of_strong_referrables.size()); + for (const auto &v : o.vector_of_strong_referrables) { vector_of_strong_referrables.emplace_back((v) ? new MyGame::Example::ReferrableT(*v) : nullptr); } + vector_of_co_owning_references.reserve(o.vector_of_co_owning_references.size()); + for (const auto &v : o.vector_of_co_owning_references) { vector_of_co_owning_references.emplace_back((v) ? new ReferrableT(*v) : nullptr); } + scalar_key_sorted_tables.reserve(o.scalar_key_sorted_tables.size()); + for (const auto &v : o.scalar_key_sorted_tables) { scalar_key_sorted_tables.emplace_back((v) ? new MyGame::Example::StatT(*v) : nullptr); } +} + +inline MonsterT &MonsterT::operator=(MonsterT o) FLATBUFFERS_NOEXCEPT { + std::swap(pos, o.pos); + std::swap(mana, o.mana); + std::swap(hp, o.hp); + std::swap(name, o.name); + std::swap(inventory, o.inventory); + std::swap(color, o.color); + std::swap(test, o.test); + std::swap(test4, o.test4); + std::swap(testarrayofstring, o.testarrayofstring); + std::swap(testarrayoftables, o.testarrayoftables); + std::swap(enemy, o.enemy); + std::swap(testnestedflatbuffer, o.testnestedflatbuffer); + std::swap(testempty, o.testempty); + std::swap(testbool, o.testbool); + std::swap(testhashs32_fnv1, o.testhashs32_fnv1); + std::swap(testhashu32_fnv1, o.testhashu32_fnv1); + std::swap(testhashs64_fnv1, o.testhashs64_fnv1); + std::swap(testhashu64_fnv1, o.testhashu64_fnv1); + std::swap(testhashs32_fnv1a, o.testhashs32_fnv1a); + std::swap(testhashu32_fnv1a, o.testhashu32_fnv1a); + std::swap(testhashs64_fnv1a, o.testhashs64_fnv1a); + std::swap(testhashu64_fnv1a, o.testhashu64_fnv1a); + std::swap(testarrayofbools, o.testarrayofbools); + std::swap(testf, o.testf); + std::swap(testf2, o.testf2); + std::swap(testf3, o.testf3); + std::swap(testarrayofstring2, o.testarrayofstring2); + std::swap(testarrayofsortedstruct, o.testarrayofsortedstruct); + std::swap(flex, o.flex); + std::swap(test5, o.test5); + std::swap(vector_of_longs, o.vector_of_longs); + std::swap(vector_of_doubles, o.vector_of_doubles); + std::swap(parent_namespace_test, o.parent_namespace_test); + std::swap(vector_of_referrables, o.vector_of_referrables); + std::swap(single_weak_reference, o.single_weak_reference); + std::swap(vector_of_weak_references, o.vector_of_weak_references); + std::swap(vector_of_strong_referrables, o.vector_of_strong_referrables); + std::swap(co_owning_reference, o.co_owning_reference); + std::swap(vector_of_co_owning_references, o.vector_of_co_owning_references); + std::swap(non_owning_reference, o.non_owning_reference); + std::swap(vector_of_non_owning_references, o.vector_of_non_owning_references); + std::swap(any_unique, o.any_unique); + std::swap(any_ambiguous, o.any_ambiguous); + std::swap(vector_of_enums, o.vector_of_enums); + std::swap(signed_enum, o.signed_enum); + std::swap(testrequirednestedflatbuffer, o.testrequirednestedflatbuffer); + std::swap(scalar_key_sorted_tables, o.scalar_key_sorted_tables); + return *this; +} + inline MonsterT *Monster::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::make_unique(); UnPackTo(_o.get(), _resolver); @@ -3011,7 +3121,7 @@ inline flatbuffers::Offset AnyUnion::Pack(flatbuffers::FlatBufferBuilder & inline AnyUnion::AnyUnion(const AnyUnion &u) : type(u.type), value(nullptr) { switch (type) { case Any::Monster: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case Any::TestSimpleTableWithEnum: { @@ -3124,7 +3234,7 @@ inline flatbuffers::Offset AnyUniqueAliasesUnion::Pack(flatbuffers::FlatBu inline AnyUniqueAliasesUnion::AnyUniqueAliasesUnion(const AnyUniqueAliasesUnion &u) : type(u.type), value(nullptr) { switch (type) { case AnyUniqueAliases::M: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyUniqueAliases::TS: { @@ -3237,15 +3347,15 @@ inline flatbuffers::Offset AnyAmbiguousAliasesUnion::Pack(flatbuffers::Fla inline AnyAmbiguousAliasesUnion::AnyAmbiguousAliasesUnion(const AnyAmbiguousAliasesUnion &u) : type(u.type), value(nullptr) { switch (type) { case AnyAmbiguousAliases::M1: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyAmbiguousAliases::M2: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyAmbiguousAliases::M3: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } default: diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index cbcbf960c..6b0615bd8 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -1233,6 +1233,10 @@ struct MonsterT : public flatbuffers::NativeTable { MyGame::Example::Race signed_enum = MyGame::Example::Race_None; std::vector testrequirednestedflatbuffer{}; std::vector> scalar_key_sorted_tables{}; + MonsterT() = default; + MonsterT(const MonsterT &o); + MonsterT(MonsterT&&) FLATBUFFERS_NOEXCEPT = default; + MonsterT &operator=(MonsterT o) FLATBUFFERS_NOEXCEPT; }; /// an example documentation comment: "monster object" @@ -2647,6 +2651,112 @@ inline bool operator!=(const MonsterT &lhs, const MonsterT &rhs) { } +inline MonsterT::MonsterT(const MonsterT &o) + : pos((o.pos) ? new MyGame::Example::Vec3(*o.pos) : nullptr), + mana(o.mana), + hp(o.hp), + name(o.name), + inventory(o.inventory), + color(o.color), + test(o.test), + test4(o.test4), + testarrayofstring(o.testarrayofstring), + enemy((o.enemy) ? new MyGame::Example::MonsterT(*o.enemy) : nullptr), + testnestedflatbuffer(o.testnestedflatbuffer), + testempty((o.testempty) ? new MyGame::Example::StatT(*o.testempty) : nullptr), + testbool(o.testbool), + testhashs32_fnv1(o.testhashs32_fnv1), + testhashu32_fnv1(o.testhashu32_fnv1), + testhashs64_fnv1(o.testhashs64_fnv1), + testhashu64_fnv1(o.testhashu64_fnv1), + testhashs32_fnv1a(o.testhashs32_fnv1a), + testhashu32_fnv1a(o.testhashu32_fnv1a), + testhashs64_fnv1a(o.testhashs64_fnv1a), + testhashu64_fnv1a(o.testhashu64_fnv1a), + testarrayofbools(o.testarrayofbools), + testf(o.testf), + testf2(o.testf2), + testf3(o.testf3), + testarrayofstring2(o.testarrayofstring2), + testarrayofsortedstruct(o.testarrayofsortedstruct), + flex(o.flex), + test5(o.test5), + vector_of_longs(o.vector_of_longs), + vector_of_doubles(o.vector_of_doubles), + parent_namespace_test((o.parent_namespace_test) ? new MyGame::InParentNamespaceT(*o.parent_namespace_test) : nullptr), + single_weak_reference(o.single_weak_reference), + vector_of_weak_references(o.vector_of_weak_references), + co_owning_reference(o.co_owning_reference), + non_owning_reference(o.non_owning_reference), + vector_of_non_owning_references(o.vector_of_non_owning_references), + any_unique(o.any_unique), + any_ambiguous(o.any_ambiguous), + vector_of_enums(o.vector_of_enums), + signed_enum(o.signed_enum), + testrequirednestedflatbuffer(o.testrequirednestedflatbuffer) { + testarrayoftables.reserve(o.testarrayoftables.size()); + for (const auto &v : o.testarrayoftables) { testarrayoftables.emplace_back((v) ? new MyGame::Example::MonsterT(*v) : nullptr); } + vector_of_referrables.reserve(o.vector_of_referrables.size()); + for (const auto &v : o.vector_of_referrables) { vector_of_referrables.emplace_back((v) ? new MyGame::Example::ReferrableT(*v) : nullptr); } + vector_of_strong_referrables.reserve(o.vector_of_strong_referrables.size()); + for (const auto &v : o.vector_of_strong_referrables) { vector_of_strong_referrables.emplace_back((v) ? new MyGame::Example::ReferrableT(*v) : nullptr); } + vector_of_co_owning_references.reserve(o.vector_of_co_owning_references.size()); + for (const auto &v : o.vector_of_co_owning_references) { vector_of_co_owning_references.emplace_back((v) ? new ReferrableT(*v) : nullptr); } + scalar_key_sorted_tables.reserve(o.scalar_key_sorted_tables.size()); + for (const auto &v : o.scalar_key_sorted_tables) { scalar_key_sorted_tables.emplace_back((v) ? new MyGame::Example::StatT(*v) : nullptr); } +} + +inline MonsterT &MonsterT::operator=(MonsterT o) FLATBUFFERS_NOEXCEPT { + std::swap(pos, o.pos); + std::swap(mana, o.mana); + std::swap(hp, o.hp); + std::swap(name, o.name); + std::swap(inventory, o.inventory); + std::swap(color, o.color); + std::swap(test, o.test); + std::swap(test4, o.test4); + std::swap(testarrayofstring, o.testarrayofstring); + std::swap(testarrayoftables, o.testarrayoftables); + std::swap(enemy, o.enemy); + std::swap(testnestedflatbuffer, o.testnestedflatbuffer); + std::swap(testempty, o.testempty); + std::swap(testbool, o.testbool); + std::swap(testhashs32_fnv1, o.testhashs32_fnv1); + std::swap(testhashu32_fnv1, o.testhashu32_fnv1); + std::swap(testhashs64_fnv1, o.testhashs64_fnv1); + std::swap(testhashu64_fnv1, o.testhashu64_fnv1); + std::swap(testhashs32_fnv1a, o.testhashs32_fnv1a); + std::swap(testhashu32_fnv1a, o.testhashu32_fnv1a); + std::swap(testhashs64_fnv1a, o.testhashs64_fnv1a); + std::swap(testhashu64_fnv1a, o.testhashu64_fnv1a); + std::swap(testarrayofbools, o.testarrayofbools); + std::swap(testf, o.testf); + std::swap(testf2, o.testf2); + std::swap(testf3, o.testf3); + std::swap(testarrayofstring2, o.testarrayofstring2); + std::swap(testarrayofsortedstruct, o.testarrayofsortedstruct); + std::swap(flex, o.flex); + std::swap(test5, o.test5); + std::swap(vector_of_longs, o.vector_of_longs); + std::swap(vector_of_doubles, o.vector_of_doubles); + std::swap(parent_namespace_test, o.parent_namespace_test); + std::swap(vector_of_referrables, o.vector_of_referrables); + std::swap(single_weak_reference, o.single_weak_reference); + std::swap(vector_of_weak_references, o.vector_of_weak_references); + std::swap(vector_of_strong_referrables, o.vector_of_strong_referrables); + std::swap(co_owning_reference, o.co_owning_reference); + std::swap(vector_of_co_owning_references, o.vector_of_co_owning_references); + std::swap(non_owning_reference, o.non_owning_reference); + std::swap(vector_of_non_owning_references, o.vector_of_non_owning_references); + std::swap(any_unique, o.any_unique); + std::swap(any_ambiguous, o.any_ambiguous); + std::swap(vector_of_enums, o.vector_of_enums); + std::swap(signed_enum, o.signed_enum); + std::swap(testrequirednestedflatbuffer, o.testrequirednestedflatbuffer); + std::swap(scalar_key_sorted_tables, o.scalar_key_sorted_tables); + return *this; +} + inline MonsterT *Monster::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new MonsterT()); UnPackTo(_o.get(), _resolver); @@ -2982,7 +3092,7 @@ inline flatbuffers::Offset AnyUnion::Pack(flatbuffers::FlatBufferBuilder & inline AnyUnion::AnyUnion(const AnyUnion &u) : type(u.type), value(nullptr) { switch (type) { case Any_Monster: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case Any_TestSimpleTableWithEnum: { @@ -3095,7 +3205,7 @@ inline flatbuffers::Offset AnyUniqueAliasesUnion::Pack(flatbuffers::FlatBu inline AnyUniqueAliasesUnion::AnyUniqueAliasesUnion(const AnyUniqueAliasesUnion &u) : type(u.type), value(nullptr) { switch (type) { case AnyUniqueAliases_M: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyUniqueAliases_TS: { @@ -3208,15 +3318,15 @@ inline flatbuffers::Offset AnyAmbiguousAliasesUnion::Pack(flatbuffers::Fla inline AnyAmbiguousAliasesUnion::AnyAmbiguousAliasesUnion(const AnyAmbiguousAliasesUnion &u) : type(u.type), value(nullptr) { switch (type) { case AnyAmbiguousAliases_M1: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyAmbiguousAliases_M2: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } case AnyAmbiguousAliases_M3: { - FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable. + value = new MyGame::Example::MonsterT(*reinterpret_cast(u.value)); break; } default: diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 73dddd7fe..ff164ccce 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -66,6 +66,10 @@ struct TableInFirstNST : public flatbuffers::NativeTable { NamespaceA::NamespaceB::EnumInNestedNS foo_enum = NamespaceA::NamespaceB::EnumInNestedNS_A; NamespaceA::NamespaceB::UnionInNestedNSUnion foo_union{}; flatbuffers::unique_ptr foo_struct{}; + TableInFirstNST() = default; + TableInFirstNST(const TableInFirstNST &o); + TableInFirstNST(TableInFirstNST&&) FLATBUFFERS_NOEXCEPT = default; + TableInFirstNST &operator=(TableInFirstNST o) FLATBUFFERS_NOEXCEPT; }; struct TableInFirstNS FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -194,6 +198,10 @@ struct TableInCT : public flatbuffers::NativeTable { } flatbuffers::unique_ptr refer_to_a1{}; flatbuffers::unique_ptr refer_to_a2{}; + TableInCT() = default; + TableInCT(const TableInCT &o); + TableInCT(TableInCT&&) FLATBUFFERS_NOEXCEPT = default; + TableInCT &operator=(TableInCT o) FLATBUFFERS_NOEXCEPT; }; struct TableInC FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -277,6 +285,10 @@ struct SecondTableInAT : public flatbuffers::NativeTable { return "NamespaceA.SecondTableInAT"; } flatbuffers::unique_ptr refer_to_c{}; + SecondTableInAT() = default; + SecondTableInAT(const SecondTableInAT &o); + SecondTableInAT(SecondTableInAT&&) FLATBUFFERS_NOEXCEPT = default; + SecondTableInAT &operator=(SecondTableInAT o) FLATBUFFERS_NOEXCEPT; }; struct SecondTableInA FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -350,6 +362,21 @@ inline bool operator!=(const TableInFirstNST &lhs, const TableInFirstNST &rhs) { } +inline TableInFirstNST::TableInFirstNST(const TableInFirstNST &o) + : foo_table((o.foo_table) ? new NamespaceA::NamespaceB::TableInNestedNST(*o.foo_table) : nullptr), + foo_enum(o.foo_enum), + foo_union(o.foo_union), + foo_struct((o.foo_struct) ? new NamespaceA::NamespaceB::StructInNestedNS(*o.foo_struct) : nullptr) { +} + +inline TableInFirstNST &TableInFirstNST::operator=(TableInFirstNST o) FLATBUFFERS_NOEXCEPT { + std::swap(foo_table, o.foo_table); + std::swap(foo_enum, o.foo_enum); + std::swap(foo_union, o.foo_union); + std::swap(foo_struct, o.foo_struct); + return *this; +} + inline TableInFirstNST *TableInFirstNS::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new TableInFirstNST()); UnPackTo(_o.get(), _resolver); @@ -404,6 +431,17 @@ inline bool operator!=(const TableInCT &lhs, const TableInCT &rhs) { } +inline TableInCT::TableInCT(const TableInCT &o) + : refer_to_a1((o.refer_to_a1) ? new NamespaceA::TableInFirstNST(*o.refer_to_a1) : nullptr), + refer_to_a2((o.refer_to_a2) ? new NamespaceA::SecondTableInAT(*o.refer_to_a2) : nullptr) { +} + +inline TableInCT &TableInCT::operator=(TableInCT o) FLATBUFFERS_NOEXCEPT { + std::swap(refer_to_a1, o.refer_to_a1); + std::swap(refer_to_a2, o.refer_to_a2); + return *this; +} + inline TableInCT *TableInC::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new TableInCT()); UnPackTo(_o.get(), _resolver); @@ -448,6 +486,15 @@ inline bool operator!=(const SecondTableInAT &lhs, const SecondTableInAT &rhs) { } +inline SecondTableInAT::SecondTableInAT(const SecondTableInAT &o) + : refer_to_c((o.refer_to_c) ? new NamespaceC::TableInCT(*o.refer_to_c) : nullptr) { +} + +inline SecondTableInAT &SecondTableInAT::operator=(SecondTableInAT o) FLATBUFFERS_NOEXCEPT { + std::swap(refer_to_c, o.refer_to_c); + return *this; +} + inline SecondTableInAT *SecondTableInA::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::unique_ptr(new SecondTableInAT()); UnPackTo(_o.get(), _resolver); diff --git a/tests/test.cpp b/tests/test.cpp index d3ce5195d..4aa9077dc 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -495,47 +495,8 @@ void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) { AccessFlatBufferTest(flatbuf, length); } -// Unpack a FlatBuffer into objects. -void ObjectFlatBuffersTest(uint8_t *flatbuf) { - // Optional: we can specify resolver and rehasher functions to turn hashed - // strings into object pointers and back, to implement remote references - // and such. - auto resolver = flatbuffers::resolver_function_t( - [](void **pointer_adr, flatbuffers::hash_value_t hash) { - (void)pointer_adr; - (void)hash; - // Don't actually do anything, leave variable null. - }); - auto rehasher = flatbuffers::rehasher_function_t( - [](void *pointer) -> flatbuffers::hash_value_t { - (void)pointer; - return 0; - }); - - // Turn a buffer into C++ objects. - auto monster1 = UnPackMonster(flatbuf, &resolver); - - // Re-serialize the data. - flatbuffers::FlatBufferBuilder fbb1; - fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher), - MonsterIdentifier()); - - // Unpack again, and re-serialize again. - auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver); - flatbuffers::FlatBufferBuilder fbb2; - fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher), - 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. +// Utility function to check a Monster object. +void CheckMonsterObject(MonsterT* monster2) { TEST_EQ(monster2->hp, 80); TEST_EQ(monster2->mana, 150); // default TEST_EQ_STR(monster2->name.c_str(), "MyMonster"); @@ -582,6 +543,63 @@ void ObjectFlatBuffersTest(uint8_t *flatbuf) { TEST_EQ(tests[1].b(), 40); } +// Unpack a FlatBuffer into objects. +void ObjectFlatBuffersTest(uint8_t *flatbuf) { + // Optional: we can specify resolver and rehasher functions to turn hashed + // strings into object pointers and back, to implement remote references + // and such. + auto resolver = flatbuffers::resolver_function_t( + [](void **pointer_adr, flatbuffers::hash_value_t hash) { + (void)pointer_adr; + (void)hash; + // Don't actually do anything, leave variable null. + }); + auto rehasher = flatbuffers::rehasher_function_t( + [](void *pointer) -> flatbuffers::hash_value_t { + (void)pointer; + return 0; + }); + + // Turn a buffer into C++ objects. + auto monster1 = UnPackMonster(flatbuf, &resolver); + + // Re-serialize the data. + flatbuffers::FlatBufferBuilder fbb1; + fbb1.Finish(CreateMonster(fbb1, monster1.get(), &rehasher), + MonsterIdentifier()); + + // Unpack again, and re-serialize again. + auto monster2 = UnPackMonster(fbb1.GetBufferPointer(), &resolver); + flatbuffers::FlatBufferBuilder fbb2; + fbb2.Finish(CreateMonster(fbb2, monster2.get(), &rehasher), + MonsterIdentifier()); + + // Now we've gone full round-trip, the two buffers should match. + const auto len1 = fbb1.GetSize(); + const 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. + CheckMonsterObject(monster2.get()); + + // Test object copy. + auto monster3 = *monster2; + flatbuffers::FlatBufferBuilder fbb3; + fbb3.Finish(CreateMonster(fbb3, &monster3, &rehasher), + MonsterIdentifier()); + const auto len3 = fbb3.GetSize(); + TEST_EQ(len2, len3); + TEST_EQ(memcmp(fbb2.GetBufferPointer(), fbb3.GetBufferPointer(), len2), 0); + // Delete monster1 and monster2, then test accessing fields in monster3. + monster1.reset(); + monster2.reset(); + CheckMonsterObject(&monster3); +} + // Prefix a FlatBuffer with a size field. void SizePrefixedTest() { // Create size prefixed buffer.