Skip to content

Commit 5998472

Browse files
Support native_type for tables when using the C++ object API. (#8668)
* 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<TableName> methods are declared but not defined; as they must be user-provided. * Add tests for native_type on tables. * Add documentation for native_type on tables.
1 parent 4173b84 commit 5998472

File tree

7 files changed

+251
-153
lines changed

7 files changed

+251
-153
lines changed

docs/source/languages/cpp.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,41 @@ provide the following functions to aide in the serialization process:
242242
}
243243
```
244244

245+
- `native_type("type")` (on a table): Tables can also be represented with
246+
native types. For example, the following schema:
247+
248+
```cpp
249+
table Matrix (native_type: "NativeMatrix") {
250+
rows: int32;
251+
columns: int32;
252+
values: [float];
253+
}
254+
```
255+
256+
Would be represented by a user-defined C++ class:
257+
258+
```cpp
259+
class NativeMatrix { ... }
260+
```
261+
262+
In this case, the following function declarations are generated by the compiler.
263+
The user must provide and link the matching function definitions:
264+
265+
```cpp
266+
struct Matrix FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
267+
// ...
268+
269+
static ::flatbuffers::Offset<Matrix> Pack(
270+
::flatbuffers::FlatBufferBuilder& _fbb,
271+
const NativeMatrix* _o,
272+
const ::flatbuffers::rehasher_function_t* _rehasher = nullptr);
273+
274+
void UnPackTo(
275+
NativeMatrix* _o,
276+
const ::flatbuffers::resolver_function_t* _resolver = nullptr) const;
277+
}
278+
```
279+
245280
Finally, the following top-level attributes:
246281

247282
- `native_include("path")` (at file level): Because the `native_type` attribute

src/idl_gen_cpp.cpp

Lines changed: 125 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,10 @@ class CppGenerator : public BaseGenerator {
501501
}
502502
}
503503

504-
if (!struct_def->fixed) {
504+
// Don't declare a new object API struct type if the table is a
505+
// native type.
506+
const auto native_type = struct_def->attributes.Lookup("native_type");
507+
if (!struct_def->fixed && !native_type) {
505508
code_ += "struct " + nativeName + ";";
506509
}
507510
}
@@ -886,12 +889,23 @@ class CppGenerator : public BaseGenerator {
886889

887890
static std::string NativeName(const std::string& name, const StructDef* sd,
888891
const IDLOptions& opts) {
892+
// If the table is a native_type, return the native_type name.
893+
const auto native_type = sd->attributes.Lookup("native_type");
894+
if (native_type && !sd->fixed) {
895+
return native_type->constant;
896+
}
897+
889898
return sd && !sd->fixed ? opts.object_prefix + name + opts.object_suffix
890899
: name;
891900
}
892901

893902
std::string WrapNativeNameInNameSpace(const StructDef& struct_def,
894903
const IDLOptions& opts) {
904+
// If the table is a native_type, return the native_type name.
905+
const auto native_type = struct_def.attributes.Lookup("native_type");
906+
if (native_type && !struct_def.fixed) {
907+
return native_type->constant;
908+
}
895909
return WrapInNameSpace(struct_def.defined_namespace,
896910
NativeName(Name(struct_def), &struct_def, opts));
897911
}
@@ -2883,14 +2897,18 @@ class CppGenerator : public BaseGenerator {
28832897

28842898
// Generate an accessor struct, builder structs & function for a table.
28852899
void GenTable(const StructDef& struct_def) {
2886-
if (opts_.generate_object_based_api) {
2900+
// Don't generate an object API struct for the table if it is a native type.
2901+
const auto native_type = struct_def.attributes.Lookup("native_type");
2902+
if (opts_.generate_object_based_api && !native_type) {
28872903
GenNativeTable(struct_def);
28882904
}
28892905

28902906
// Generate an accessor struct, with methods of the form:
28912907
// type name() const { return GetField<type>(offset, defaultval); }
28922908
GenComment(struct_def.doc_comment);
28932909

2910+
const auto native_name = NativeName(Name(struct_def), &struct_def, opts_);
2911+
code_.SetValue("NATIVE_NAME", native_name);
28942912
code_.SetValue("STRUCT_NAME", Name(struct_def));
28952913
code_ +=
28962914
"struct {{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS"
@@ -3766,7 +3784,9 @@ class CppGenerator : public BaseGenerator {
37663784

37673785
// Generate code for tables that needs to come after the regular definition.
37683786
void GenTablePost(const StructDef& struct_def) {
3769-
if (opts_.generate_object_based_api) {
3787+
// Don't generate an object API struct for the table if it is a native type.
3788+
const auto native_type = struct_def.attributes.Lookup("native_type");
3789+
if (opts_.generate_object_based_api && !native_type) {
37703790
GenNativeTablePost(struct_def);
37713791
}
37723792

@@ -3776,7 +3796,9 @@ class CppGenerator : public BaseGenerator {
37763796

37773797
if (opts_.generate_object_based_api) {
37783798
// Generate the >= C++11 copy ctor and assignment operator definitions.
3779-
GenCopyCtorAssignOpDefs(struct_def);
3799+
if (!native_type) {
3800+
GenCopyCtorAssignOpDefs(struct_def);
3801+
}
37803802

37813803
// Generate the X::UnPack() method.
37823804
code_ +=
@@ -3799,33 +3821,38 @@ class CppGenerator : public BaseGenerator {
37993821
code_ += " return _o.release();";
38003822
code_ += "}";
38013823
code_ += "";
3802-
code_ +=
3803-
"inline " + TableUnPackToSignature(struct_def, false, opts_) + " {";
3804-
code_ += " (void)_o;";
3805-
code_ += " (void)_resolver;";
38063824

3807-
for (auto it = struct_def.fields.vec.begin();
3808-
it != struct_def.fields.vec.end(); ++it) {
3809-
const auto& field = **it;
3810-
if (field.deprecated) {
3811-
continue;
3812-
}
3825+
// Generate an Unpack method for the C++ object if that table does not
3826+
// have a native type.
3827+
if (!native_type) {
3828+
code_ +=
3829+
"inline " + TableUnPackToSignature(struct_def, false, opts_) + " {";
3830+
code_ += " (void)_o;";
3831+
code_ += " (void)_resolver;";
3832+
3833+
for (auto it = struct_def.fields.vec.begin();
3834+
it != struct_def.fields.vec.end(); ++it) {
3835+
const auto& field = **it;
3836+
if (field.deprecated) {
3837+
continue;
3838+
}
38133839

3814-
// Assign a value from |this| to |_o|. Values from |this| are stored
3815-
// in a variable |_e| by calling this->field_type(). The value is then
3816-
// assigned to |_o| using the GenUnpackFieldStatement.
3817-
const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE;
3818-
const auto statement =
3819-
GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr);
3820-
3821-
code_.SetValue("FIELD_NAME", Name(field));
3822-
auto prefix = " { auto _e = {{FIELD_NAME}}(); ";
3823-
auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) ";
3824-
auto postfix = " }";
3825-
code_ += std::string(prefix) + check + statement + postfix;
3840+
// Assign a value from |this| to |_o|. Values from |this| are stored
3841+
// in a variable |_e| by calling this->field_type(). The value is
3842+
// then assigned to |_o| using the GenUnpackFieldStatement.
3843+
const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE;
3844+
const auto statement =
3845+
GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr);
3846+
3847+
code_.SetValue("FIELD_NAME", Name(field));
3848+
auto prefix = " { auto _e = {{FIELD_NAME}}(); ";
3849+
auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) ";
3850+
auto postfix = " }";
3851+
code_ += std::string(prefix) + check + statement + postfix;
3852+
}
3853+
code_ += "}";
3854+
code_ += "";
38263855
}
3827-
code_ += "}";
3828-
code_ += "";
38293856

38303857
// Generate the global CreateX function that simply calls the
38313858
// X::Pack member function.
@@ -3835,78 +3862,85 @@ class CppGenerator : public BaseGenerator {
38353862
code_ += "}";
38363863
code_ += "";
38373864

3838-
// Generate a CreateX method that works with an unpacked C++ object.
3839-
code_ += "inline " + TablePackSignature(struct_def, false, opts_) + " {";
3840-
code_ += " (void)_rehasher;";
3841-
code_ += " (void)_o;";
3865+
if (!native_type) {
3866+
// Generate a Pack method that works with an unpacked C++ object if it
3867+
// does not have a native type.
3868+
code_ +=
3869+
"inline " + TablePackSignature(struct_def, false, opts_) + " {";
3870+
code_ += " (void)_rehasher;";
3871+
code_ += " (void)_o;";
38423872

3843-
code_ +=
3844-
" struct _VectorArgs "
3845-
"{ " +
3846-
GetBuilder() +
3847-
" *__fbb; "
3848-
"const " +
3849-
NativeName(Name(struct_def), &struct_def, opts_) +
3850-
"* __o; "
3851-
"const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { "
3852-
"&_fbb, _o, _rehasher}; (void)_va;";
3853-
3854-
for (auto it = struct_def.fields.vec.begin();
3855-
it != struct_def.fields.vec.end(); ++it) {
3856-
auto& field = **it;
3857-
if (field.deprecated) {
3858-
continue;
3859-
}
3860-
if (IsVector(field.value.type)) {
3861-
const std::string force_align_code =
3862-
GenVectorForceAlign(field, "_o->" + Name(field) + ".size()");
3863-
if (!force_align_code.empty()) {
3864-
code_ += " " + force_align_code;
3873+
code_ +=
3874+
" struct _VectorArgs "
3875+
"{ " +
3876+
GetBuilder() +
3877+
" *__fbb; "
3878+
"const " +
3879+
NativeName(Name(struct_def), &struct_def, opts_) +
3880+
"* __o; "
3881+
"const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { "
3882+
"&_fbb, _o, _rehasher}; (void)_va;";
3883+
3884+
for (auto it = struct_def.fields.vec.begin();
3885+
it != struct_def.fields.vec.end(); ++it) {
3886+
auto& field = **it;
3887+
if (field.deprecated) {
3888+
continue;
38653889
}
3890+
if (IsVector(field.value.type)) {
3891+
const std::string force_align_code =
3892+
GenVectorForceAlign(field, "_o->" + Name(field) + ".size()");
3893+
if (!force_align_code.empty()) {
3894+
code_ += " " + force_align_code;
3895+
}
3896+
}
3897+
code_ +=
3898+
" auto _" + Name(field) + " = " + GenCreateParam(field) + ";";
38663899
}
3867-
code_ += " auto _" + Name(field) + " = " + GenCreateParam(field) + ";";
3868-
}
3869-
// Need to call "Create" with the struct namespace.
3870-
const auto qualified_create_name =
3871-
struct_def.defined_namespace->GetFullyQualifiedName("Create");
3872-
code_.SetValue("CREATE_NAME", TranslateNameSpace(qualified_create_name));
3873-
3874-
code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
3875-
code_ += " _fbb\\";
3876-
for (const auto& field : struct_def.fields.vec) {
3877-
if (field->deprecated) {
3878-
continue;
3879-
}
3900+
// Need to call "Create" with the struct namespace.
3901+
const auto qualified_create_name =
3902+
struct_def.defined_namespace->GetFullyQualifiedName("Create");
3903+
code_.SetValue("CREATE_NAME",
3904+
TranslateNameSpace(qualified_create_name));
3905+
3906+
code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
3907+
code_ += " _fbb\\";
3908+
for (const auto& field : struct_def.fields.vec) {
3909+
if (field->deprecated) {
3910+
continue;
3911+
}
38803912

3881-
bool pass_by_address = false;
3882-
bool check_ptr = false;
3883-
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
3884-
if (IsStruct(field->value.type)) {
3885-
auto native_type =
3886-
field->value.type.struct_def->attributes.Lookup("native_type");
3887-
auto native_inline = field->attributes.Lookup("native_inline");
3888-
if (native_type) {
3889-
pass_by_address = true;
3890-
}
3891-
if (native_type && !native_inline) {
3892-
check_ptr = true;
3913+
bool pass_by_address = false;
3914+
bool check_ptr = false;
3915+
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
3916+
if (IsStruct(field->value.type)) {
3917+
auto native_type =
3918+
field->value.type.struct_def->attributes.Lookup(
3919+
"native_type");
3920+
auto native_inline = field->attributes.Lookup("native_inline");
3921+
if (native_type) {
3922+
pass_by_address = true;
3923+
}
3924+
if (native_type && !native_inline) {
3925+
check_ptr = true;
3926+
}
38933927
}
38943928
}
3895-
}
38963929

3897-
// Call the CreateX function using values from |_o|.
3898-
if (pass_by_address && check_ptr) {
3899-
code_ += ",\n _o->" + Name(*field) + " ? &_" + Name(*field) +
3900-
" : nullptr\\";
3901-
} else if (pass_by_address) {
3902-
code_ += ",\n &_" + Name(*field) + "\\";
3903-
} else {
3904-
code_ += ",\n _" + Name(*field) + "\\";
3930+
// Call the CreateX function using values from |_o|.
3931+
if (pass_by_address && check_ptr) {
3932+
code_ += ",\n _o->" + Name(*field) + " ? &_" + Name(*field) +
3933+
" : nullptr\\";
3934+
} else if (pass_by_address) {
3935+
code_ += ",\n &_" + Name(*field) + "\\";
3936+
} else {
3937+
code_ += ",\n _" + Name(*field) + "\\";
3938+
}
39053939
}
3940+
code_ += ");";
3941+
code_ += "}";
3942+
code_ += "";
39063943
}
3907-
code_ += ");";
3908-
code_ += "}";
3909-
code_ += "";
39103944
}
39113945
}
39123946

tests/native_type_test.fbs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ struct Vector3DAlt (native_type:"Native::Vector3D", native_type_pack_name:"Vecto
1414
c:float;
1515
}
1616

17-
// table Matrix (native_type:"Native::Matrix") {
18-
table Matrix {
17+
table Matrix (native_type:"Native::Matrix") {
1918
rows:int32;
2019
columns:int32;
2120
values:[float];

0 commit comments

Comments
 (0)