From 9ea9d0a7c6fd1e95c43a412ef70fae0b198a0cd6 Mon Sep 17 00:00:00 2001 From: Connor Smith Date: Tue, 12 Aug 2025 17:33:15 -0700 Subject: [PATCH 1/4] Support native_type for tables when using the C++ object API. If native_type is specified on a table: - No object API struct type is generated. - The object API refers to the table by its native_type. - UnPack and Create methods are declared but not defined; as they must be user-provided. --- src/idl_gen_cpp.cpp | 207 ++++++++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 84 deletions(-) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 1fa06652523..f108e071137 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -485,7 +485,12 @@ class CppGenerator : public BaseGenerator { } } - if (!struct_def->fixed) { code_ += "struct " + nativeName + ";"; } + // Don't declare a new object API struct type if the table is a + // native type. + const auto native_type = struct_def->attributes.Lookup("native_type"); + if (!struct_def->fixed && !native_type) { + code_ += "struct " + nativeName + ";"; + } } code_ += ""; } @@ -863,12 +868,23 @@ class CppGenerator : public BaseGenerator { static std::string NativeName(const std::string &name, const StructDef *sd, const IDLOptions &opts) { + // If the table is a native_type, return the native_type name. + const auto native_type = sd->attributes.Lookup("native_type"); + if (native_type && !sd->fixed) { + return native_type->constant; + } + return sd && !sd->fixed ? opts.object_prefix + name + opts.object_suffix : name; } std::string WrapNativeNameInNameSpace(const StructDef &struct_def, const IDLOptions &opts) { + // If the table is a native_type, return the native_type name. + const auto native_type = struct_def.attributes.Lookup("native_type"); + if (native_type && !struct_def.fixed) { + return native_type->constant; + } return WrapInNameSpace(struct_def.defined_namespace, NativeName(Name(struct_def), &struct_def, opts)); } @@ -2793,12 +2809,18 @@ class CppGenerator : public BaseGenerator { // Generate an accessor struct, builder structs & function for a table. void GenTable(const StructDef &struct_def) { - if (opts_.generate_object_based_api) { GenNativeTable(struct_def); } + // Don't generate an object API struct for the table if it is a native type. + const auto native_type = struct_def.attributes.Lookup("native_type"); + if (opts_.generate_object_based_api && !native_type) { + GenNativeTable(struct_def); + } // Generate an accessor struct, with methods of the form: // type name() const { return GetField(offset, defaultval); } GenComment(struct_def.doc_comment); + const auto native_name = NativeName(Name(struct_def), &struct_def, opts_); + code_.SetValue("NATIVE_NAME", native_name); code_.SetValue("STRUCT_NAME", Name(struct_def)); code_ += "struct {{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS" @@ -3642,7 +3664,11 @@ class CppGenerator : public BaseGenerator { // Generate code for tables that needs to come after the regular definition. void GenTablePost(const StructDef &struct_def) { - if (opts_.generate_object_based_api) { GenNativeTablePost(struct_def); } + // Don't generate an object API struct for the table if it is a native type. + const auto native_type = struct_def.attributes.Lookup("native_type"); + if (opts_.generate_object_based_api && !native_type) { + GenNativeTablePost(struct_def); + } code_.SetValue("STRUCT_NAME", Name(struct_def)); code_.SetValue("NATIVE_NAME", @@ -3650,7 +3676,9 @@ class CppGenerator : public BaseGenerator { if (opts_.generate_object_based_api) { // Generate the >= C++11 copy ctor and assignment operator definitions. - GenCopyCtorAssignOpDefs(struct_def); + if (!native_type) { + GenCopyCtorAssignOpDefs(struct_def); + } // Generate the X::UnPack() method. code_ += @@ -3673,31 +3701,36 @@ class CppGenerator : public BaseGenerator { code_ += " return _o.release();"; code_ += "}"; code_ += ""; - code_ += - "inline " + TableUnPackToSignature(struct_def, false, opts_) + " {"; - code_ += " (void)_o;"; - code_ += " (void)_resolver;"; - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - const auto &field = **it; - if (field.deprecated) { continue; } - - // Assign a value from |this| to |_o|. Values from |this| are stored - // in a variable |_e| by calling this->field_type(). The value is then - // assigned to |_o| using the GenUnpackFieldStatement. - const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE; - const auto statement = - GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr); - - code_.SetValue("FIELD_NAME", Name(field)); - auto prefix = " { auto _e = {{FIELD_NAME}}(); "; - auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) "; - auto postfix = " }"; - code_ += std::string(prefix) + check + statement + postfix; + // Generate an Unpack method for the C++ object if that table does not + // have a native type. + if (!native_type) { + code_ += + "inline " + TableUnPackToSignature(struct_def, false, opts_) + " {"; + code_ += " (void)_o;"; + code_ += " (void)_resolver;"; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + const auto &field = **it; + if (field.deprecated) { continue; } + + // Assign a value from |this| to |_o|. Values from |this| are stored + // in a variable |_e| by calling this->field_type(). The value is + // then assigned to |_o| using the GenUnpackFieldStatement. + const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE; + const auto statement = + GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr); + + code_.SetValue("FIELD_NAME", Name(field)); + auto prefix = " { auto _e = {{FIELD_NAME}}(); "; + auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) "; + auto postfix = " }"; + code_ += std::string(prefix) + check + statement + postfix; + } + code_ += "}"; + code_ += ""; } - code_ += "}"; - code_ += ""; // Generate the X::Pack member function that simply calls the global // CreateX function. @@ -3706,69 +3739,75 @@ class CppGenerator : public BaseGenerator { code_ += "}"; code_ += ""; - // Generate a CreateX method that works with an unpacked C++ object. - code_ += - "inline " + TableCreateSignature(struct_def, false, opts_) + " {"; - code_ += " (void)_rehasher;"; - code_ += " (void)_o;"; - - code_ += - " struct _VectorArgs " - "{ " + - GetBuilder() + - " *__fbb; " - "const " + - NativeName(Name(struct_def), &struct_def, opts_) + - "* __o; " - "const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { " - "&_fbb, _o, _rehasher}; (void)_va;"; - - for (auto it = struct_def.fields.vec.begin(); - it != struct_def.fields.vec.end(); ++it) { - auto &field = **it; - if (field.deprecated) { continue; } - if (IsVector(field.value.type)) { - const std::string force_align_code = - GenVectorForceAlign(field, "_o->" + Name(field) + ".size()"); - if (!force_align_code.empty()) { code_ += " " + force_align_code; } - } - code_ += " auto _" + Name(field) + " = " + GenCreateParam(field) + ";"; - } - // Need to call "Create" with the struct namespace. - const auto qualified_create_name = - struct_def.defined_namespace->GetFullyQualifiedName("Create"); - code_.SetValue("CREATE_NAME", TranslateNameSpace(qualified_create_name)); - - code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}("; - code_ += " _fbb\\"; - for (const auto &field : struct_def.fields.vec) { - if (field->deprecated) { continue; } + if (!native_type) { + // Generate a CreateX method that works with an unpacked C++ object if + // it does not have a native type. + code_ += + "inline " + TableCreateSignature(struct_def, false, opts_) + " {"; + code_ += " (void)_rehasher;"; + code_ += " (void)_o;"; - bool pass_by_address = false; - bool check_ptr = false; - if (field->value.type.base_type == BASE_TYPE_STRUCT) { - if (IsStruct(field->value.type)) { - auto native_type = - field->value.type.struct_def->attributes.Lookup("native_type"); - auto native_inline = field->attributes.Lookup("native_inline"); - if (native_type) { pass_by_address = true; } - if (native_type && !native_inline) { check_ptr = true; } + code_ += + " struct _VectorArgs " + "{ " + + GetBuilder() + + " *__fbb; " + "const " + + NativeName(Name(struct_def), &struct_def, opts_) + + "* __o; " + "const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { " + "&_fbb, _o, _rehasher}; (void)_va;"; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) { continue; } + if (IsVector(field.value.type)) { + const std::string force_align_code = + GenVectorForceAlign(field, "_o->" + Name(field) + ".size()"); + if (!force_align_code.empty()) { code_ += " " + force_align_code; } } + code_ += + " auto _" + Name(field) + " = " + GenCreateParam(field) + ";"; } + // Need to call "Create" with the struct namespace. + const auto qualified_create_name = + struct_def.defined_namespace->GetFullyQualifiedName("Create"); + code_.SetValue("CREATE_NAME", + TranslateNameSpace(qualified_create_name)); + + code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}("; + code_ += " _fbb\\"; + for (const auto &field : struct_def.fields.vec) { + if (field->deprecated) { continue; } + + bool pass_by_address = false; + bool check_ptr = false; + if (field->value.type.base_type == BASE_TYPE_STRUCT) { + if (IsStruct(field->value.type)) { + auto native_type = + field->value.type.struct_def->attributes.Lookup( + "native_type"); + auto native_inline = field->attributes.Lookup("native_inline"); + if (native_type) { pass_by_address = true; } + if (native_type && !native_inline) { check_ptr = true; } + } + } - // Call the CreateX function using values from |_o|. - if (pass_by_address && check_ptr) { - code_ += ",\n _o->" + Name(*field) + " ? &_" + Name(*field) + - " : nullptr\\"; - } else if (pass_by_address) { - code_ += ",\n &_" + Name(*field) + "\\"; - } else { - code_ += ",\n _" + Name(*field) + "\\"; + // Call the CreateX function using values from |_o|. + if (pass_by_address && check_ptr) { + code_ += ",\n _o->" + Name(*field) + " ? &_" + Name(*field) + + " : nullptr\\"; + } else if (pass_by_address) { + code_ += ",\n &_" + Name(*field) + "\\"; + } else { + code_ += ",\n _" + Name(*field) + "\\"; + } } + code_ += ");"; + code_ += "}"; + code_ += ""; } - code_ += ");"; - code_ += "}"; - code_ += ""; } } From 2d4e53742caddb9a271abac529b3f8136a113e03 Mon Sep 17 00:00:00 2001 From: Connor Smith Date: Tue, 12 Aug 2025 17:38:32 -0700 Subject: [PATCH 2/4] Add tests for native_type on tables. --- tests/native_type_test.fbs | 8 ++ tests/native_type_test_generated.h | 197 +++++++++++++++++++++++++++-- tests/native_type_test_impl.cpp | 31 +++++ tests/native_type_test_impl.h | 16 +++ tests/test.cpp | 23 ++++ 5 files changed, 265 insertions(+), 10 deletions(-) diff --git a/tests/native_type_test.fbs b/tests/native_type_test.fbs index 2b7d5474e97..1e73d33305c 100644 --- a/tests/native_type_test.fbs +++ b/tests/native_type_test.fbs @@ -14,11 +14,19 @@ struct Vector3DAlt (native_type:"Native::Vector3D", native_type_pack_name:"Vecto c:float; } +table Matrix (native_type:"Native::Matrix") { + rows:int32; + columns:int32; + values:[float]; +} + table ApplicationData { vectors:[Vector3D]; vectors_alt:[Vector3DAlt]; position:Vector3D; position_inline:Vector3D (native_inline); + matrix:Matrix; + matrices:[Matrix]; } root_type ApplicationData; diff --git a/tests/native_type_test_generated.h b/tests/native_type_test_generated.h index 55585f32479..1aee1a76729 100644 --- a/tests/native_type_test_generated.h +++ b/tests/native_type_test_generated.h @@ -21,6 +21,9 @@ struct Vector3D; struct Vector3DAlt; +struct Matrix; +struct MatrixBuilder; + struct ApplicationData; struct ApplicationDataBuilder; struct ApplicationDataT; @@ -29,6 +32,8 @@ inline const ::flatbuffers::TypeTable *Vector3DTypeTable(); inline const ::flatbuffers::TypeTable *Vector3DAltTypeTable(); +inline const ::flatbuffers::TypeTable *MatrixTypeTable(); + inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable(); FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Vector3D FLATBUFFERS_FINAL_CLASS { @@ -113,12 +118,107 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Vector3DAlt FLATBUFFERS_FINAL_CLASS { }; FLATBUFFERS_STRUCT_END(Vector3DAlt, 12); +struct Matrix FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + typedef Native::Matrix NativeTableType; + typedef MatrixBuilder Builder; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { + return MatrixTypeTable(); + } + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROWS = 4, + VT_COLUMNS = 6, + VT_VALUES = 8 + }; + int32_t rows() const { + return GetField(VT_ROWS, 0); + } + bool mutate_rows(int32_t _rows = 0) { + return SetField(VT_ROWS, _rows, 0); + } + int32_t columns() const { + return GetField(VT_COLUMNS, 0); + } + bool mutate_columns(int32_t _columns = 0) { + return SetField(VT_COLUMNS, _columns, 0); + } + const ::flatbuffers::Vector *values() const { + return GetPointer *>(VT_VALUES); + } + ::flatbuffers::Vector *mutable_values() { + return GetPointer<::flatbuffers::Vector *>(VT_VALUES); + } + bool Verify(::flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROWS, 4) && + VerifyField(verifier, VT_COLUMNS, 4) && + VerifyOffset(verifier, VT_VALUES) && + verifier.VerifyVector(values()) && + verifier.EndTable(); + } + Native::Matrix *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(Native::Matrix *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; + static ::flatbuffers::Offset Pack(::flatbuffers::FlatBufferBuilder &_fbb, const Native::Matrix* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct MatrixBuilder { + typedef Matrix Table; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_rows(int32_t rows) { + fbb_.AddElement(Matrix::VT_ROWS, rows, 0); + } + void add_columns(int32_t columns) { + fbb_.AddElement(Matrix::VT_COLUMNS, columns, 0); + } + void add_values(::flatbuffers::Offset<::flatbuffers::Vector> values) { + fbb_.AddOffset(Matrix::VT_VALUES, values); + } + explicit MatrixBuilder(::flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ::flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = ::flatbuffers::Offset(end); + return o; + } +}; + +inline ::flatbuffers::Offset CreateMatrix( + ::flatbuffers::FlatBufferBuilder &_fbb, + int32_t rows = 0, + int32_t columns = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> values = 0) { + MatrixBuilder builder_(_fbb); + builder_.add_values(values); + builder_.add_columns(columns); + builder_.add_rows(rows); + return builder_.Finish(); +} + +inline ::flatbuffers::Offset CreateMatrixDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + int32_t rows = 0, + int32_t columns = 0, + const std::vector *values = nullptr) { + auto values__ = values ? _fbb.CreateVector(*values) : 0; + return Geometry::CreateMatrix( + _fbb, + rows, + columns, + values__); +} + +::flatbuffers::Offset CreateMatrix(::flatbuffers::FlatBufferBuilder &_fbb, const Native::Matrix *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct ApplicationDataT : public ::flatbuffers::NativeTable { typedef ApplicationData TableType; std::vector vectors{}; std::vector vectors_alt{}; std::unique_ptr position{}; Native::Vector3D position_inline{}; + std::unique_ptr matrix{}; + std::vector> matrices{}; ApplicationDataT() = default; ApplicationDataT(const ApplicationDataT &o); ApplicationDataT(ApplicationDataT&&) FLATBUFFERS_NOEXCEPT = default; @@ -135,7 +235,9 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VT_VECTORS = 4, VT_VECTORS_ALT = 6, VT_POSITION = 8, - VT_POSITION_INLINE = 10 + VT_POSITION_INLINE = 10, + VT_MATRIX = 12, + VT_MATRICES = 14 }; const ::flatbuffers::Vector *vectors() const { return GetPointer *>(VT_VECTORS); @@ -161,6 +263,18 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { Geometry::Vector3D *mutable_position_inline() { return GetStruct(VT_POSITION_INLINE); } + const Geometry::Matrix *matrix() const { + return GetPointer(VT_MATRIX); + } + Geometry::Matrix *mutable_matrix() { + return GetPointer(VT_MATRIX); + } + const ::flatbuffers::Vector<::flatbuffers::Offset> *matrices() const { + return GetPointer> *>(VT_MATRICES); + } + ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_matrices() { + return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_MATRICES); + } bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_VECTORS) && @@ -169,6 +283,11 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { verifier.VerifyVector(vectors_alt()) && VerifyField(verifier, VT_POSITION, 4) && VerifyField(verifier, VT_POSITION_INLINE, 4) && + VerifyOffset(verifier, VT_MATRIX) && + verifier.VerifyTable(matrix()) && + VerifyOffset(verifier, VT_MATRICES) && + verifier.VerifyVector(matrices()) && + verifier.VerifyVectorOfTables(matrices()) && verifier.EndTable(); } ApplicationDataT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -192,6 +311,12 @@ struct ApplicationDataBuilder { void add_position_inline(const Geometry::Vector3D *position_inline) { fbb_.AddStruct(ApplicationData::VT_POSITION_INLINE, position_inline); } + void add_matrix(::flatbuffers::Offset matrix) { + fbb_.AddOffset(ApplicationData::VT_MATRIX, matrix); + } + void add_matrices(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> matrices) { + fbb_.AddOffset(ApplicationData::VT_MATRICES, matrices); + } explicit ApplicationDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -208,8 +333,12 @@ inline ::flatbuffers::Offset CreateApplicationData( ::flatbuffers::Offset<::flatbuffers::Vector> vectors = 0, ::flatbuffers::Offset<::flatbuffers::Vector> vectors_alt = 0, const Geometry::Vector3D *position = nullptr, - const Geometry::Vector3D *position_inline = nullptr) { + const Geometry::Vector3D *position_inline = nullptr, + ::flatbuffers::Offset matrix = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> matrices = 0) { ApplicationDataBuilder builder_(_fbb); + builder_.add_matrices(matrices); + builder_.add_matrix(matrix); builder_.add_position_inline(position_inline); builder_.add_position(position); builder_.add_vectors_alt(vectors_alt); @@ -222,24 +351,42 @@ inline ::flatbuffers::Offset CreateApplicationDataDirect( const std::vector *vectors = nullptr, const std::vector *vectors_alt = nullptr, const Geometry::Vector3D *position = nullptr, - const Geometry::Vector3D *position_inline = nullptr) { + const Geometry::Vector3D *position_inline = nullptr, + ::flatbuffers::Offset matrix = 0, + const std::vector<::flatbuffers::Offset> *matrices = nullptr) { auto vectors__ = vectors ? _fbb.CreateVectorOfStructs(*vectors) : 0; auto vectors_alt__ = vectors_alt ? _fbb.CreateVectorOfStructs(*vectors_alt) : 0; + auto matrices__ = matrices ? _fbb.CreateVector<::flatbuffers::Offset>(*matrices) : 0; return Geometry::CreateApplicationData( _fbb, vectors__, vectors_alt__, position, - position_inline); + position_inline, + matrix, + matrices__); } ::flatbuffers::Offset CreateApplicationData(::flatbuffers::FlatBufferBuilder &_fbb, const ApplicationDataT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); +inline Native::Matrix *Matrix::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::unique_ptr(new Native::Matrix()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline ::flatbuffers::Offset Matrix::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const Native::Matrix* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { + return CreateMatrix(_fbb, _o, _rehasher); +} + inline ApplicationDataT::ApplicationDataT(const ApplicationDataT &o) : vectors(o.vectors), vectors_alt(o.vectors_alt), position((o.position) ? new Native::Vector3D(*o.position) : nullptr), - position_inline(o.position_inline) { + position_inline(o.position_inline), + matrix((o.matrix) ? new Native::Matrix(*o.matrix) : nullptr) { + matrices.reserve(o.matrices.size()); + for (const auto &matrices_ : o.matrices) { matrices.emplace_back((matrices_) ? new Native::Matrix(*matrices_) : nullptr); } } inline ApplicationDataT &ApplicationDataT::operator=(ApplicationDataT o) FLATBUFFERS_NOEXCEPT { @@ -247,6 +394,8 @@ inline ApplicationDataT &ApplicationDataT::operator=(ApplicationDataT o) FLATBUF std::swap(vectors_alt, o.vectors_alt); std::swap(position, o.position); std::swap(position_inline, o.position_inline); + std::swap(matrix, o.matrix); + std::swap(matrices, o.matrices); return *this; } @@ -263,6 +412,8 @@ inline void ApplicationData::UnPackTo(ApplicationDataT *_o, const ::flatbuffers: { auto _e = vectors_alt(); if (_e) { _o->vectors_alt.resize(_e->size()); for (::flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->vectors_alt[_i] = ::flatbuffers::UnPackVector3DAlt(*_e->Get(_i)); } } else { _o->vectors_alt.resize(0); } } { auto _e = position(); if (_e) _o->position = std::unique_ptr(new Native::Vector3D(::flatbuffers::UnPack(*_e))); } { auto _e = position_inline(); if (_e) _o->position_inline = ::flatbuffers::UnPack(*_e); } + { auto _e = matrix(); if (_e) { if(_o->matrix) { _e->UnPackTo(_o->matrix.get(), _resolver); } else { _o->matrix = std::unique_ptr(_e->UnPack(_resolver)); } } else if (_o->matrix) { _o->matrix.reset(); } } + { auto _e = matrices(); if (_e) { _o->matrices.resize(_e->size()); for (::flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { if(_o->matrices[_i]) { _e->Get(_i)->UnPackTo(_o->matrices[_i].get(), _resolver); } else { _o->matrices[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } else { _o->matrices.resize(0); } } } inline ::flatbuffers::Offset ApplicationData::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const ApplicationDataT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { @@ -277,12 +428,16 @@ inline ::flatbuffers::Offset CreateApplicationData(::flatbuffer auto _vectors_alt = _o->vectors_alt.size() ? _fbb.CreateVectorOfNativeStructs(_o->vectors_alt, ::flatbuffers::PackVector3DAlt) : 0; auto _position = Geometry::Vector3D{}; if (_o->position) _position = ::flatbuffers::Pack(*_o->position); auto _position_inline = ::flatbuffers::Pack(_o->position_inline); + auto _matrix = _o->matrix ? CreateMatrix(_fbb, _o->matrix.get(), _rehasher) : 0; + auto _matrices = _o->matrices.size() ? _fbb.CreateVector<::flatbuffers::Offset> (_o->matrices.size(), [](size_t i, _VectorArgs *__va) { return CreateMatrix(*__va->__fbb, __va->__o->matrices[i].get(), __va->__rehasher); }, &_va ) : 0; return Geometry::CreateApplicationData( _fbb, _vectors, _vectors_alt, _o->position ? &_position : nullptr, - &_position_inline); + &_position_inline, + _matrix, + _matrices); } inline const ::flatbuffers::TypeTable *Vector3DTypeTable() { @@ -321,25 +476,47 @@ inline const ::flatbuffers::TypeTable *Vector3DAltTypeTable() { return &tt; } +inline const ::flatbuffers::TypeTable *MatrixTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 1, -1 } + }; + static const char * const names[] = { + "rows", + "columns", + "values" + }; + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 3, type_codes, nullptr, nullptr, nullptr, names + }; + return &tt; +} + inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable() { static const ::flatbuffers::TypeCode type_codes[] = { { ::flatbuffers::ET_SEQUENCE, 1, 0 }, { ::flatbuffers::ET_SEQUENCE, 1, 1 }, { ::flatbuffers::ET_SEQUENCE, 0, 0 }, - { ::flatbuffers::ET_SEQUENCE, 0, 0 } + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 1, 2 } }; static const ::flatbuffers::TypeFunction type_refs[] = { Geometry::Vector3DTypeTable, - Geometry::Vector3DAltTypeTable + Geometry::Vector3DAltTypeTable, + Geometry::MatrixTypeTable }; static const char * const names[] = { "vectors", "vectors_alt", "position", - "position_inline" + "position_inline", + "matrix", + "matrices" }; static const ::flatbuffers::TypeTable tt = { - ::flatbuffers::ST_TABLE, 4, type_codes, type_refs, nullptr, nullptr, names + ::flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/tests/native_type_test_impl.cpp b/tests/native_type_test_impl.cpp index b5b595b8488..f0dfd6e8cb0 100644 --- a/tests/native_type_test_impl.cpp +++ b/tests/native_type_test_impl.cpp @@ -19,3 +19,34 @@ const Native::Vector3D UnPackVector3DAlt(const Geometry::Vector3DAlt &obj) { return Native::Vector3D(obj.a(), obj.b(), obj.c()); } } // namespace flatbuffers + +namespace Geometry { +void Matrix::UnPackTo( + Native::Matrix *_o, + const ::flatbuffers::resolver_function_t *_resolver) const { + (void)_resolver; + + auto _rows = rows(); + if (_rows) { _o->rows = _rows; } + + auto _columns = columns(); + if (_columns) { _o->columns = _columns; } + + auto _values = values(); + if (_values) { + _o->values.resize(_values->size()); + for (::flatbuffers::uoffset_t i = 0; i < _values->size(); i++) { + _o->values[i] = _values->Get(i); + } + } +} + +::flatbuffers::Offset CreateMatrix( + ::flatbuffers::FlatBufferBuilder &_fbb, const Native::Matrix *_o, + const ::flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + + return CreateMatrix(_fbb, _o->rows, _o->columns, + _fbb.CreateVector(_o->values)); +} +} // namespace Geometry diff --git a/tests/native_type_test_impl.h b/tests/native_type_test_impl.h index bc8f385ec66..d1e05ba0d36 100644 --- a/tests/native_type_test_impl.h +++ b/tests/native_type_test_impl.h @@ -1,6 +1,8 @@ #ifndef NATIVE_TYPE_TEST_IMPL_H #define NATIVE_TYPE_TEST_IMPL_H +#include + namespace Native { struct Vector3D { float x; @@ -18,6 +20,20 @@ struct Vector3D { this->z = _z; } }; + +struct Matrix { + int rows; + int columns; + std::vector values; + + Matrix() : Matrix(0, 0) {} + + Matrix(int _rows, int _columns) { + this->rows = _rows; + this->columns = _columns; + values.resize(_rows * _columns); + } +}; } // namespace Native namespace Geometry { diff --git a/tests/test.cpp b/tests/test.cpp index 1564fa7064d..fae08108246 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -922,6 +922,15 @@ void NativeTypeTest() { Native::Vector3D(20 * i + 0.1f, 20 * i + 0.2f, 20 * i + 0.3f)); } + src_data.matrix = std::make_unique(1, 2); + src_data.matrix->values = {3, 4}; + + for (int i = 0; i < N; ++i) { + src_data.matrices.push_back(std::make_unique(1, i)); + std::fill(src_data.matrices[i]->values.begin(), + src_data.matrices[i]->values.end(), i + 0.5f); + } + flatbuffers::FlatBufferBuilder fbb; fbb.Finish(Geometry::ApplicationData::Pack(fbb, &src_data)); @@ -945,6 +954,20 @@ void NativeTypeTest() { TEST_EQ(v2.y, 20 * i + 0.2f); TEST_EQ(v2.z, 20 * i + 0.3f); } + + TEST_EQ(dstDataT->matrix->rows, 1); + TEST_EQ(dstDataT->matrix->columns, 2); + TEST_EQ(dstDataT->matrix->values[0], 3); + TEST_EQ(dstDataT->matrix->values[1], 4); + + for (int i = 0; i < N; ++i) { + const Native::Matrix &m = *dstDataT->matrices[i]; + TEST_EQ(m.rows, 1); + TEST_EQ(m.columns, i); + for (int j = 0; j < i; ++j) { + TEST_EQ(m.values[j], i + 0.5f); + } + } } // Guard against -Wunused-function on platforms without file tests. From dbf2cce9ec81b280013932fe06c36946f2aee822 Mon Sep 17 00:00:00 2001 From: Connor Smith Date: Tue, 12 Aug 2025 17:38:49 -0700 Subject: [PATCH 3/4] Add documentation for native_type on tables. --- docs/source/languages/cpp.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/source/languages/cpp.md b/docs/source/languages/cpp.md index 7e99d4efd15..c5c81539749 100644 --- a/docs/source/languages/cpp.md +++ b/docs/source/languages/cpp.md @@ -242,6 +242,40 @@ provide the following functions to aide in the serialization process: } ``` +- `native_type("type")` (on a table): Tables can also be represented with +native types. For example, the following schema: + +```cpp + table Matrix { + rows: int32; + columns: int32; + values: [float]; + } +``` + +Could be represented by a user-defined C++ class: + +```cpp + class Matrix { ... } +``` + +In this case, the following function declarations are generated by the compiler. +The user must provide and link the matching function definitions: + +```cpp + struct Matrix FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { + // ... + void UnPackTo( + Native::Matrix *_o, + const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; + } + + ::flatbuffers::Offset CreateMatrix( + ::flatbuffers::FlatBufferBuilder &_fbb, + const NativeMatrixType *_o, + const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); +``` + Finally, the following top-level attributes: - `native_include("path")` (at file level): Because the `native_type` attribute From 6adcd8c1d1e13d879d054b1e415656d90204dd1f Mon Sep 17 00:00:00 2001 From: Connor Smith Date: Tue, 19 Aug 2025 12:15:17 -0700 Subject: [PATCH 4/4] Fix --gen-compare to not generate comparators for native types. Per the definition of --gen-compare: only generate comparators for object API generated structs. Tables and structs annoated with native_type must define their own comparators if `--gen-compare` is enabled. --- scripts/generate_code.py | 2 +- src/idl_gen_cpp.cpp | 9 ++++++++- tests/native_type_test_generated.h | 19 +++++++++++++++++++ tests/native_type_test_impl.h | 9 +++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/scripts/generate_code.py b/scripts/generate_code.py index 500a3706e7a..6ad077bffdc 100755 --- a/scripts/generate_code.py +++ b/scripts/generate_code.py @@ -334,7 +334,7 @@ def glob(path, pattern): ) flatc( - ["--cpp", "--gen-mutable", "--gen-object-api", "--reflect-names"], + ["--cpp", "--gen-mutable", "--gen-object-api", "--reflect-names", "--gen-compare"], schema="native_type_test.fbs", ) diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index f108e071137..11d9eb93ab6 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -499,7 +499,8 @@ class CppGenerator : public BaseGenerator { // Generate forward declarations for all equal operators if (opts_.generate_object_based_api && opts_.gen_compare) { for (const auto &struct_def : parser_.structs_.vec) { - if (!struct_def->generated) { + const auto native_type = struct_def->attributes.Lookup("native_type"); + if (!struct_def->generated && !native_type) { SetNameSpace(struct_def->defined_namespace); auto nativeName = NativeName(Name(*struct_def), struct_def, opts_); code_ += "bool operator==(const " + nativeName + " &lhs, const " + @@ -2144,6 +2145,12 @@ class CppGenerator : public BaseGenerator { void GenCompareOperator(const StructDef &struct_def, const std::string &accessSuffix = "") { + // Do not generate compare operators for native types. + const auto native_type = struct_def.attributes.Lookup("native_type"); + if (native_type) { + return; + } + std::string compare_op; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { diff --git a/tests/native_type_test_generated.h b/tests/native_type_test_generated.h index 1aee1a76729..0337f57bed1 100644 --- a/tests/native_type_test_generated.h +++ b/tests/native_type_test_generated.h @@ -28,6 +28,9 @@ struct ApplicationData; struct ApplicationDataBuilder; struct ApplicationDataT; +bool operator==(const ApplicationDataT &lhs, const ApplicationDataT &rhs); +bool operator!=(const ApplicationDataT &lhs, const ApplicationDataT &rhs); + inline const ::flatbuffers::TypeTable *Vector3DTypeTable(); inline const ::flatbuffers::TypeTable *Vector3DAltTypeTable(); @@ -379,6 +382,22 @@ inline ::flatbuffers::Offset Matrix::Pack(::flatbuffers::FlatBufferBuild return CreateMatrix(_fbb, _o, _rehasher); } + +inline bool operator==(const ApplicationDataT &lhs, const ApplicationDataT &rhs) { + return + (lhs.vectors == rhs.vectors) && + (lhs.vectors_alt == rhs.vectors_alt) && + ((lhs.position == rhs.position) || (lhs.position && rhs.position && *lhs.position == *rhs.position)) && + (lhs.position_inline == rhs.position_inline) && + ((lhs.matrix == rhs.matrix) || (lhs.matrix && rhs.matrix && *lhs.matrix == *rhs.matrix)) && + (lhs.matrices.size() == rhs.matrices.size() && std::equal(lhs.matrices.cbegin(), lhs.matrices.cend(), rhs.matrices.cbegin(), [](std::unique_ptr const &a, std::unique_ptr const &b) { return (a == b) || (a && b && *a == *b); })); +} + +inline bool operator!=(const ApplicationDataT &lhs, const ApplicationDataT &rhs) { + return !(lhs == rhs); +} + + inline ApplicationDataT::ApplicationDataT(const ApplicationDataT &o) : vectors(o.vectors), vectors_alt(o.vectors_alt), diff --git a/tests/native_type_test_impl.h b/tests/native_type_test_impl.h index d1e05ba0d36..603b8ab30a5 100644 --- a/tests/native_type_test_impl.h +++ b/tests/native_type_test_impl.h @@ -19,6 +19,10 @@ struct Vector3D { this->y = _y; this->z = _z; } + + bool operator==(const Vector3D &other) const { + return (x == other.x) && (y == other.y) && (z == other.z); + } }; struct Matrix { @@ -33,6 +37,11 @@ struct Matrix { this->columns = _columns; values.resize(_rows * _columns); } + + bool operator==(const Matrix &other) const { + return (rows == other.rows) && (columns == other.columns) && + (values == other.values); + } }; } // namespace Native