[C++] Support C++ object copies and moves (#5988)
authorJean-François Roy <jf@devklog.net>
Sat, 29 Jan 2022 22:24:24 +0000 (14:24 -0800)
committerGitHub <noreply@github.com>
Sat, 29 Jan 2022 22:24:24 +0000 (14:24 -0800)
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

samples/monster_generated.h
src/idl_gen_cpp.cpp
tests/arrays_test_generated.h
tests/cpp17/generated_cpp17/monster_test_generated.h
tests/monster_test_generated.h
tests/namespace_test/namespace_test2_generated.h
tests/test.cpp

index b3fc655..13b8c3e 100644 (file)
@@ -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);
index a14ac77..e1386fd 100644 (file)
@@ -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_) + " {";
index 007a5f5..1300b92 100644 (file)
@@ -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);
index ad09dac..f201c85 100644 (file)
@@ -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:
index cbcbf96..6b0615b 100644 (file)
@@ -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:
index 73dddd7..ff164cc 100644 (file)
@@ -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);
index d3ce519..4aa9077 100644 (file)
@@ -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.