summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-François Roy <jf@devklog.net>2022-01-29 14:24:24 -0800
committerGitHub <noreply@github.com>2022-01-29 14:24:24 -0800
commit43203984f7e7095435e849ad09c363b16325aa2b (patch)
treeb2bb4b42f80dbb2d0ea073714c4972604fd4fa0f
parent5993338ee378836f47ca31ee0a57a58e287d3a65 (diff)
downloadflatbuffers-43203984f7e7095435e849ad09c363b16325aa2b.tar.gz
flatbuffers-43203984f7e7095435e849ad09c363b16325aa2b.tar.bz2
flatbuffers-43203984f7e7095435e849ad09c363b16325aa2b.zip
[C++] Support C++ object copies and moves (#5988)
Augment the C++ generator to emit a C++ copy constructor and a by-value copy assignment operator. This is enabled by default when the C++ standard is C++11 or later. These additional functions are only emitted for objects that need it, typically tables containing other tables. These new functions are declared in the object table type and are defined as inline functions after table declarations. When these new functions are declared, a user-defined explicitly-defaulted default constructor and move constructor are also emitted. The copy assignment operator uses the copy-and-swap idiom to provide strong exception safety, at the expense of keeping 2 full table copies in memory temporarily. fixes #5783
-rw-r--r--samples/monster_generated.h30
-rw-r--r--src/idl_gen_cpp.cpp176
-rw-r--r--tests/arrays_test_generated.h13
-rw-r--r--tests/cpp17/generated_cpp17/monster_test_generated.h120
-rw-r--r--tests/monster_test_generated.h120
-rw-r--r--tests/namespace_test/namespace_test2_generated.h47
-rw-r--r--tests/test.cpp100
7 files changed, 547 insertions, 59 deletions
diff --git a/samples/monster_generated.h b/samples/monster_generated.h
index b3fc655b..13b8c3ea 100644
--- a/samples/monster_generated.h
+++ b/samples/monster_generated.h
@@ -239,6 +239,10 @@ struct MonsterT : public flatbuffers::NativeTable {
std::vector<flatbuffers::unique_ptr<MyGame::Sample::WeaponT>> weapons{};
MyGame::Sample::EquipmentUnion equipped{};
std::vector<MyGame::Sample::Vec3> 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<MonsterT>(new MonsterT());
UnPackTo(_o.get(), _resolver);
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
index a14ac77a..e1386fdf 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 007a5f5b..1300b927 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<MyGame::Example::ArrayStruct> 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<ArrayTableT>(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 ad09dacc..f201c85d 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<uint8_t> testrequirednestedflatbuffer{};
std::vector<std::unique_ptr<MyGame::Example::StatT>> 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<Referrable> 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<MonsterT>();
UnPackTo(_o.get(), _resolver);
@@ -3011,7 +3121,7 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case Any::TestSimpleTableWithEnum: {
@@ -3124,7 +3234,7 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyUniqueAliases::TS: {
@@ -3237,15 +3347,15 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyAmbiguousAliases::M2: {
- FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable.
+ value = new MyGame::Example::MonsterT(*reinterpret_cast<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyAmbiguousAliases::M3: {
- FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable.
+ value = new MyGame::Example::MonsterT(*reinterpret_cast<MyGame::Example::MonsterT *>(u.value));
break;
}
default:
diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h
index cbcbf960..6b0615bd 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<uint8_t> testrequirednestedflatbuffer{};
std::vector<flatbuffers::unique_ptr<MyGame::Example::StatT>> 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<MonsterT>(new MonsterT());
UnPackTo(_o.get(), _resolver);
@@ -2982,7 +3092,7 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case Any_TestSimpleTableWithEnum: {
@@ -3095,7 +3205,7 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyUniqueAliases_TS: {
@@ -3208,15 +3318,15 @@ inline flatbuffers::Offset<void> 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<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyAmbiguousAliases_M2: {
- FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable.
+ value = new MyGame::Example::MonsterT(*reinterpret_cast<MyGame::Example::MonsterT *>(u.value));
break;
}
case AnyAmbiguousAliases_M3: {
- FLATBUFFERS_ASSERT(false); // MyGame::Example::MonsterT not copyable.
+ value = new MyGame::Example::MonsterT(*reinterpret_cast<MyGame::Example::MonsterT *>(u.value));
break;
}
default:
diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h
index 73dddd7f..ff164ccc 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<NamespaceA::NamespaceB::StructInNestedNS> 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<NamespaceA::TableInFirstNST> refer_to_a1{};
flatbuffers::unique_ptr<NamespaceA::SecondTableInAT> 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<NamespaceC::TableInCT> 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<TableInFirstNST>(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<TableInCT>(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<SecondTableInAT>(new SecondTableInAT());
UnPackTo(_o.get(), _resolver);
diff --git a/tests/test.cpp b/tests/test.cpp
index d3ce5195..4aa9077d 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.