Support FromFlatbuffer() with Object API
This adds support so that FromFlatbuffer works on FlatbufferT types (the
"object" API), with the same semantics as Flatbuffer::Pack().
This makes it so that existing code which uses these types can easily be
translated into the new API.
Change-Id: I11b2e259708a15bdd62e215dfa514fcb9450ade5
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/flatbuffers/static_flatbuffers.cc b/aos/flatbuffers/static_flatbuffers.cc
index 8d8942c..70228e5 100644
--- a/aos/flatbuffers/static_flatbuffers.cc
+++ b/aos/flatbuffers/static_flatbuffers.cc
@@ -19,6 +19,8 @@
bool is_inline = true;
// Whether this is a struct or not.
bool is_struct = false;
+ // Whether this is a repeated type (vector or string).
+ bool is_repeated = false;
// Full C++ type of this field.
std::string full_type = "";
// Full flatbuffer type for this field.
@@ -151,12 +153,14 @@
// straightforwards.
field->is_inline = true;
field->is_struct = false;
+ field->is_repeated = false;
field->full_type =
ScalarOrEnumType(schema, type->base_type(), type->index());
return;
case reflection::BaseType::String: {
field->is_inline = false;
field->is_struct = false;
+ field->is_repeated = true;
field->full_type =
absl::StrFormat("::aos::fbs::String<%d>",
GetLengthAttributeOrZero(field_fbs, "static_length"));
@@ -166,6 +170,7 @@
// We need to extract the name of the elements of the vector.
std::string element_type;
bool elements_are_inline = true;
+ field->is_repeated = true;
if (type->base_type() == reflection::BaseType::Vector) {
switch (type->element()) {
case reflection::BaseType::Obj: {
@@ -207,6 +212,7 @@
const reflection::Object *object = GetObject(schema, type->index());
field->is_inline = object->is_struct();
field->is_struct = object->is_struct();
+ field->is_repeated = false;
const std::string flatbuffer_name =
FlatbufferNameToCppName(object->name()->string_view());
if (field->is_inline) {
@@ -437,6 +443,76 @@
absl::StrJoin(clearers, "\n"));
}
+// Creates the FromFlatbuffer() method that copies from a flatbuffer object API
+// object (i.e., the FlatbufferT types).
+std::string MakeObjectCopier(const std::vector<FieldData> &fields) {
+ std::vector<std::string> copiers;
+ for (const FieldData &field : fields) {
+ if (field.is_struct) {
+ // Structs are stored as unique_ptr<FooStruct>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ set_%s(*other.%s);
+ }
+ )code",
+ field.name, field.name, field.name));
+ } else if (field.is_inline) {
+ // Inline non-struct elements are stored as FooType.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ set_%s(other.%s);
+ )code",
+ field.name, field.name));
+ } else if (field.is_repeated) {
+ // strings are stored as std::string's.
+ // vectors are stored as std::vector's.
+ copiers.emplace_back(absl::StrFormat(R"code(
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ )code",
+ field.name, field.name));
+ } else {
+ // Tables are stored as unique_ptr<FooTable>
+ copiers.emplace_back(absl::StrFormat(R"code(
+ if (other.%s) {
+ if (!CHECK_NOTNULL(add_%s())->FromFlatbuffer(*other.%s)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+ )code",
+ field.name, field.name, field.name));
+ }
+ }
+ return absl::StrFormat(
+ R"code(
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+ %s
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(const flatbuffers::unique_ptr<Flatbuffer::NativeTableType>& other) {
+ return FromFlatbuffer(*other);
+ }
+)code",
+ absl::StrJoin(copiers, "\n"));
+}
+
+// Creates the FromFlatbuffer() method that copies from an actual flatbuffer
+// object.
std::string MakeCopier(const std::vector<FieldData> &fields) {
std::vector<std::string> copiers;
for (const FieldData &field : fields) {
@@ -689,6 +765,7 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef %s Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType> FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -699,6 +776,7 @@
%s
%s
%s
+%s
private:
%s
%s
@@ -708,7 +786,7 @@
)code",
type_namespace, type_name, FlatbufferNameToCppName(fbs_type_name),
constants, MakeConstructor(type_name), type_name, accessors,
- MakeFullClearer(fields), MakeCopier(fields),
+ MakeFullClearer(fields), MakeCopier(fields), MakeObjectCopier(fields),
MakeMoveConstructor(type_name), members, MakeSubObjectList(fields));
GeneratedObject result;
diff --git a/aos/flatbuffers/static_flatbuffers_test.cc b/aos/flatbuffers/static_flatbuffers_test.cc
index fd95db3..ca1cf42 100644
--- a/aos/flatbuffers/static_flatbuffers_test.cc
+++ b/aos/flatbuffers/static_flatbuffers_test.cc
@@ -1042,4 +1042,50 @@
TestMemory(builder.buffer());
}
+// Uses a small example to manually verify that we can copy from the flatbuffer
+// object API.
+TEST_F(StaticFlatbuffersTest, ObjectApiCopy) {
+ aos::fbs::testing::TestTableT object_t;
+ object_t.scalar = 971;
+ object_t.vector_of_strings.push_back("971");
+ object_t.vector_of_structs.push_back({1, 2});
+ object_t.subtable = std::make_unique<SubTableT>();
+ aos::fbs::VectorAllocator allocator;
+ Builder<TestTableStatic> builder(&allocator);
+ ASSERT_TRUE(builder->FromFlatbuffer(object_t));
+ ASSERT_TRUE(builder.AsFlatbufferSpan().Verify());
+ // Note that vectors and strings get set to zero-length, but present, values.
+ EXPECT_EQ(
+ "{ \"scalar\": 971, \"vector_of_scalars\": [ ], \"string\": \"\", "
+ "\"vector_of_strings\": [ \"971\" ], \"subtable\": { \"foo\": 0, "
+ "\"baz\": 0.0 }, \"vector_aligned\": [ ], \"vector_of_structs\": [ { "
+ "\"x\": 1.0, \"y\": 2.0 } ], \"vector_of_tables\": [ ], "
+ "\"unspecified_length_vector\": [ ], \"unspecified_length_string\": "
+ "\"\", \"unspecified_length_vector_of_strings\": [ ] }",
+ aos::FlatbufferToJson(builder.AsFlatbufferSpan()));
+}
+
+// More completely covers our object API copying by comparing the flatbuffer
+// Pack() methods to our FromFlatbuffer() methods.
+TEST_F(StaticFlatbuffersTest, FlatbufferObjectTypeCoverage) {
+ VerifyJson<aos::testing::ConfigurationStatic>("{\n\n}");
+ std::string populated_config =
+ aos::util::ReadFileToStringOrDie(aos::testing::ArtifactPath(
+ "aos/flatbuffers/test_dir/type_coverage.json"));
+ Builder<aos::testing::ConfigurationStatic> json_builder =
+ aos::JsonToStaticFlatbuffer<aos::testing::ConfigurationStatic>(
+ populated_config);
+ aos::testing::ConfigurationT object_t;
+ json_builder->AsFlatbuffer().UnPackTo(&object_t);
+
+ Builder<aos::testing::ConfigurationStatic> from_object_static;
+ ASSERT_TRUE(from_object_static->FromFlatbuffer(object_t));
+ flatbuffers::FlatBufferBuilder fbb;
+ fbb.Finish(aos::testing::Configuration::Pack(fbb, &object_t));
+ aos::FlatbufferDetachedBuffer<aos::testing::Configuration> from_object_raw =
+ fbb.Release();
+ EXPECT_EQ(aos::FlatbufferToJson(from_object_raw, {.multi_line = true}),
+ aos::FlatbufferToJson(from_object_static, {.multi_line = true}));
+}
+
} // namespace aos::fbs::testing
diff --git a/aos/flatbuffers/static_vector.h b/aos/flatbuffers/static_vector.h
index 7349a8d..bf40ce5 100644
--- a/aos/flatbuffers/static_vector.h
+++ b/aos/flatbuffers/static_vector.h
@@ -203,6 +203,11 @@
typename internal::InlineWrapper<T, kInline>::FlatbufferType;
using ConstFlatbufferType =
typename internal::InlineWrapper<T, kInline>::ConstFlatbufferType;
+ // FlatbufferObjectType corresponds to the type used by the flatbuffer
+ // "object" API (i.e. the FlatbufferT types).
+ // This type will be something unintelligble for inline types.
+ using FlatbufferObjectType =
+ typename internal::InlineWrapper<T, kInline>::FlatbufferObjectType;
// flatbuffers::Vector type that corresponds to this Vector.
typedef flatbuffers::Vector<FlatbufferType> Flatbuffer;
typedef const flatbuffers::Vector<ConstFlatbufferType> ConstFlatbuffer;
@@ -330,6 +335,66 @@
// This is a deep copy, and will call FromFlatbuffer on any constituent
// objects.
[[nodiscard]] bool FromFlatbuffer(ConstFlatbuffer *vector);
+ // The remaining FromFlatbuffer() overloads are for when using the flatbuffer
+ // "object" API, which uses std::vector's for representing vectors.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<InlineType> &vector) {
+ static_assert(kInline);
+ return FromData(vector.data(), vector.size());
+ }
+ // Overload for vectors of bools, since the standard library may not use a
+ // full byte per vector element.
+ [[nodiscard]] bool FromFlatbuffer(const std::vector<bool> &vector) {
+ static_assert(kInline);
+ // We won't be able to do a clean memcpy because std::vector<bool> may be
+ // implemented using bit-packing.
+ return FromIterator(vector.cbegin(), vector.cend());
+ }
+ // Overload for non-inline types. Note that to avoid having this overload get
+ // resolved with inline types, we make FlatbufferObjectType != InlineType.
+ [[nodiscard]] bool FromFlatbuffer(
+ const std::vector<FlatbufferObjectType> &vector) {
+ static_assert(!kInline);
+ return FromNotInlineIterable(vector);
+ }
+
+ // Copies values from the provided data pointer into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ [[nodiscard]] bool FromData(const InlineType *input_data, size_t input_size) {
+ static_assert(kInline);
+ if (!reserve(input_size)) {
+ return false;
+ }
+
+ // We will be overwriting the whole vector very shortly; there is no need to
+ // clear the buffer to zero.
+ resize_inline(input_size, SetZero::kNo);
+
+ memcpy(inline_data(), input_data, size() * sizeof(InlineType));
+ return true;
+ }
+
+ // Copies values from the provided iterators into the vector, resizing the
+ // vector as needed to match. Returns false on failure (e.g., if the
+ // underlying allocator has insufficient space to perform the copy). Only
+ // works for inline data types.
+ // Does not attempt any optimizations if the iterators meet the
+ // std::contiguous_iterator concept; instead, it simply copies each element
+ // out one-by-one.
+ template <typename Iterator>
+ [[nodiscard]] bool FromIterator(Iterator begin, Iterator end) {
+ static_assert(kInline);
+ resize(0);
+ for (Iterator it = begin; it != end; ++it) {
+ if (!reserve(size() + 1)) {
+ return false;
+ }
+ // Should never fail, due to the reserve() above.
+ CHECK(emplace_back(*it));
+ }
+ return true;
+ }
// Returns the element at the provided index. index must be less than size().
const T &at(size_t index) const {
@@ -570,28 +635,22 @@
// Implementation that handles copying from a flatbuffers::Vector of an inline
// data type.
[[nodiscard]] bool FromInlineFlatbuffer(ConstFlatbuffer *vector) {
- if (!reserve(CHECK_NOTNULL(vector)->size())) {
- return false;
- }
-
- // We will be overwriting the whole vector very shortly; there is no need to
- // clear the buffer to zero.
- resize_inline(vector->size(), SetZero::kNo);
-
- memcpy(inline_data(), vector->Data(), size() * sizeof(InlineType));
- return true;
+ return FromData(
+ reinterpret_cast<const InlineType *>(CHECK_NOTNULL(vector)->Data()),
+ vector->size());
}
// Implementation that handles copying from a flatbuffers::Vector of a
// not-inline data type.
- [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
- if (!reserve(vector->size())) {
+ template <typename Iterable>
+ [[nodiscard]] bool FromNotInlineIterable(const Iterable &vector) {
+ if (!reserve(vector.size())) {
return false;
}
// "Clear" the vector.
resize_not_inline(0);
- for (const typename T::Flatbuffer *entry : *vector) {
+ for (const auto &entry : vector) {
if (!CHECK_NOTNULL(emplace_back())->FromFlatbuffer(entry)) {
return false;
}
@@ -599,6 +658,10 @@
return true;
}
+ [[nodiscard]] bool FromNotInlineFlatbuffer(const Flatbuffer *vector) {
+ return FromNotInlineIterable(*vector);
+ }
+
// In order to allow for easy partial template specialization, we use a
// non-member class to call FromInline/FromNotInlineFlatbuffer and
// resize_inline/resize_not_inline. There are not actually any great ways to
@@ -659,6 +722,7 @@
public:
typedef Vector<char, kStaticLength, true, 0, true> VectorType;
typedef flatbuffers::String Flatbuffer;
+ typedef std::string FlatbufferObjectType;
String(std::span<uint8_t> buffer, ResizeableObject *parent)
: VectorType(buffer, parent) {}
virtual ~String() {}
@@ -667,6 +731,10 @@
VectorType::resize_inline(string.size(), SetZero::kNo);
memcpy(VectorType::data(), string.data(), string.size());
}
+ using VectorType::FromFlatbuffer;
+ [[nodiscard]] bool FromFlatbuffer(const std::string &string) {
+ return VectorType::FromData(string.data(), string.size());
+ }
std::string_view string_view() const {
return std::string_view(VectorType::data(), VectorType::size());
}
@@ -690,6 +758,7 @@
typedef T ObjectType;
typedef flatbuffers::Offset<typename T::Flatbuffer> FlatbufferType;
typedef flatbuffers::Offset<typename T::Flatbuffer> ConstFlatbufferType;
+ typedef T::FlatbufferObjectType FlatbufferObjectType;
static_assert((T::kSize % T::kAlign) == 0);
static constexpr size_t kDataAlign = T::kAlign;
static constexpr size_t kDataSize = T::kSize;
@@ -712,6 +781,7 @@
typedef T ObjectType;
typedef T FlatbufferType;
typedef T ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
@@ -731,6 +801,7 @@
typedef uint8_t ObjectType;
typedef uint8_t FlatbufferType;
typedef uint8_t ConstFlatbufferType;
+ typedef uint8_t *FlatbufferObjectType;
static constexpr size_t kDataAlign = 1u;
static constexpr size_t kDataSize = 1u;
template <typename StaticVector>
@@ -753,6 +824,7 @@
typedef T ObjectType;
typedef T *FlatbufferType;
typedef const T *ConstFlatbufferType;
+ typedef T *FlatbufferObjectType;
static constexpr size_t kDataAlign = alignof(T);
static constexpr size_t kDataSize = sizeof(T);
template <typename StaticVector>
diff --git a/aos/flatbuffers/test_dir/sample_test_static.h b/aos/flatbuffers/test_dir/sample_test_static.h
index a6362d7..8fc97da 100644
--- a/aos/flatbuffers/test_dir/sample_test_static.h
+++ b/aos/flatbuffers/test_dir/sample_test_static.h
@@ -14,6 +14,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::MinimallyAlignedTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -145,6 +147,25 @@
return true;
}
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_field(other.field);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
+
private:
// We need to provide a MoveConstructor to allow this table to be
// used inside of vectors, but we do not want it readily available to
@@ -168,6 +189,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::SubTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -328,6 +351,27 @@
return true;
}
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ set_baz(other.baz);
+
+ set_foo(other.foo);
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
+
private:
// We need to provide a MoveConstructor to allow this table to be
// used inside of vectors, but we do not want it readily available to
@@ -354,6 +398,8 @@
public:
// The underlying "raw" flatbuffer type for this type.
typedef aos::fbs::testing::TestTable Flatbuffer;
+ typedef flatbuffers::unique_ptr<Flatbuffer::NativeTableType>
+ FlatbufferObjectType;
// Returns this object as a flatbuffer type. This reference may not be valid
// following mutations to the underlying flatbuffer, due to how memory may get
// may get moved around.
@@ -1172,6 +1218,135 @@
return true;
}
+ // Copies the contents of the provided flatbuffer into this flatbuffer,
+ // returning true on success.
+ // Because the Flatbuffer Object API does not provide any concept of an
+ // optionally populated scalar field, all scalar fields will be populated
+ // after a call to FromFlatbufferObject().
+ // This is a deep copy, and will call FromFlatbufferObject on
+ // any constituent objects.
+ [[nodiscard]] bool FromFlatbuffer(const Flatbuffer::NativeTableType &other) {
+ Clear();
+
+ if (other.included_table) {
+ if (!CHECK_NOTNULL(add_included_table())
+ ->FromFlatbuffer(*other.included_table)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ set_scalar(other.scalar);
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_string())->FromFlatbuffer(other.string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ if (other.substruct) {
+ set_substruct(*other.substruct);
+ }
+
+ if (other.subtable) {
+ if (!CHECK_NOTNULL(add_subtable())->FromFlatbuffer(*other.subtable)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_string())
+ ->FromFlatbuffer(other.unspecified_length_string)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector())
+ ->FromFlatbuffer(other.unspecified_length_vector)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_unspecified_length_vector_of_strings())
+ ->FromFlatbuffer(other.unspecified_length_vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_aligned())
+ ->FromFlatbuffer(other.vector_aligned)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_scalars())
+ ->FromFlatbuffer(other.vector_of_scalars)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_strings())
+ ->FromFlatbuffer(other.vector_of_strings)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_structs())
+ ->FromFlatbuffer(other.vector_of_structs)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ // Unconditionally copy strings/vectors, even if it will just end up
+ // being 0-length (this maintains consistency with the flatbuffer Pack()
+ // behavior).
+ if (!CHECK_NOTNULL(add_vector_of_tables())
+ ->FromFlatbuffer(other.vector_of_tables)) {
+ // Fail if we were unable to copy (e.g., if we tried to copy in a long
+ // vector and do not have the space for it).
+ return false;
+ }
+
+ return true;
+ }
+ [[nodiscard]] bool FromFlatbuffer(
+ const flatbuffers::unique_ptr<Flatbuffer::NativeTableType> &other) {
+ return FromFlatbuffer(*other);
+ }
+
private:
// We need to provide a MoveConstructor to allow this table to be
// used inside of vectors, but we do not want it readily available to