diff --git a/CMakeLists.txt b/CMakeLists.txt index 05831f192..456d9fbe6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,8 @@ set(FlatBuffers_Tests_SRCS ${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_bfbs_generated.h # file generate by running compiler on tests/optional_scalars.fbs ${CMAKE_CURRENT_BINARY_DIR}/tests/optional_scalars_generated.h + # file generate by running compiler on tests/native_inline_table_test.fbs + ${CMAKE_CURRENT_BINARY_DIR}/tests/native_inline_table_test_generated.h ) set(FlatBuffers_Tests_CPP17_SRCS @@ -620,6 +622,7 @@ if(FLATBUFFERS_BUILD_TESTS) compile_flatbuffers_schema_to_cpp_opt(tests/arrays_test.fbs "--scoped-enums;--gen-compare") compile_flatbuffers_schema_to_binary(tests/arrays_test.fbs) compile_flatbuffers_schema_to_embedded_binary(tests/monster_test.fbs "--no-includes;--gen-compare") + compile_flatbuffers_schema_to_cpp(tests/native_inline_table_test.fbs "--gen-compare") if(NOT (MSVC AND (MSVC_VERSION LESS 1900))) compile_flatbuffers_schema_to_cpp(tests/monster_extra.fbs) # Test floating-point NAN/INF. endif() diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index c27c1c154..4cfd7ebfb 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -464,6 +464,10 @@ inline bool IsStruct(const Type &type) { return type.base_type == BASE_TYPE_STRUCT && type.struct_def->fixed; } +inline bool IsTable(const Type &type) { + return type.base_type == BASE_TYPE_STRUCT && !type.struct_def->fixed; +} + inline bool IsUnion(const Type &type) { return type.enum_def != nullptr && type.enum_def->is_union; } @@ -476,6 +480,14 @@ inline bool IsVector(const Type &type) { return type.base_type == BASE_TYPE_VECTOR; } +inline bool IsVectorOfStruct(const Type& type) { + return IsVector(type) && IsStruct(type.VectorType()); +} + +inline bool IsVectorOfTable(const Type& type) { + return IsVector(type) && IsTable(type.VectorType()); +} + inline bool IsArray(const Type &type) { return type.base_type == BASE_TYPE_ARRAY; } diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index db5a814ae..67d6a0a77 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -74,6 +74,15 @@ static std::string GenIncludeGuard(const std::string &file_name, return guard; } +static bool IsVectorOfPointers(const FieldDef& field) { + const auto& type = field.value.type; + const auto& vector_type = type.VectorType(); + return type.base_type == BASE_TYPE_VECTOR && + vector_type.base_type == BASE_TYPE_STRUCT && + !vector_type.struct_def->fixed && + !field.native_inline; +} + namespace cpp { enum CppStandard { CPP_STD_X0 = 0, CPP_STD_11, CPP_STD_17 }; @@ -885,7 +894,9 @@ class CppGenerator : public BaseGenerator { } } else { const auto nn = WrapNativeNameInNameSpace(*type.struct_def, opts_); - return forcopy ? nn : GenTypeNativePtr(nn, &field, false); + return (forcopy || field.native_inline) + ? nn + : GenTypeNativePtr(nn, &field, false); } } case BASE_TYPE_UNION: { @@ -1871,9 +1882,7 @@ class CppGenerator : public BaseGenerator { 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"); + const bool is_ptr = IsVectorOfPointers(field) || (cpp_type && cpp_ptr_type->constant != "naked"); if (is_ptr) { return true; } } } @@ -1997,9 +2006,7 @@ class CppGenerator : public BaseGenerator { ? 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"); + const bool is_ptr = IsVectorOfPointers(field) || (cpp_type && cpp_ptr_type->constant != "naked"); CodeWriter cw(" "); cw.SetValue("FIELD", Name(field)); cw.SetValue("TYPE", type_name); @@ -2078,9 +2085,7 @@ class CppGenerator : public BaseGenerator { // If the field is a vector of tables, the table need to be compared // by value, instead of by the default unique_ptr == operator which // compares by address. - if (field.value.type.base_type == BASE_TYPE_VECTOR && - field.value.type.element == BASE_TYPE_STRUCT && - !field.value.type.struct_def->fixed) { + if (IsVectorOfPointers(field)) { const auto type = GenTypeNative(field.value.type.VectorType(), true, field); const auto equal_length = @@ -2979,7 +2984,8 @@ class CppGenerator : public BaseGenerator { return ptype + "(new " + name + "(*" + val + "))"; } } else { - const auto ptype = GenTypeNativePtr( + std::string ptype = afield.native_inline ? "*" : ""; + ptype += GenTypeNativePtr( WrapNativeNameInNameSpace(*type.struct_def, opts_), &afield, true); return ptype + "(" + val + "->UnPack(_resolver))"; @@ -3066,9 +3072,7 @@ class CppGenerator : public BaseGenerator { } else { // clang-format off #if FLATBUFFERS_CPP_OBJECT_UNPACKTO - const bool is_pointer = - field.value.type.VectorType().base_type == BASE_TYPE_STRUCT && - !IsStruct(field.value.type.VectorType()); + const bool is_pointer = IsVectorOfPointers(field); if (is_pointer) { code += "if(_o->" + name + "[_i]" + ") { "; code += indexing + "->UnPackTo(_o->" + name + @@ -3131,9 +3135,7 @@ class CppGenerator : public BaseGenerator { // _o->field = value; // clang-format off #if FLATBUFFERS_CPP_OBJECT_UNPACKTO - const bool is_pointer = - field.value.type.base_type == BASE_TYPE_STRUCT && - !IsStruct(field.value.type); + const bool is_pointer = IsVectorOfPointers(field); if (is_pointer) { code += "{ if(_o->" + Name(field) + ") { "; code += "_e->UnPackTo(_o->" + Name(field) + ".get(), _resolver);"; @@ -3251,9 +3253,13 @@ class CppGenerator : public BaseGenerator { code += "(" + value + ".size(), "; code += "[](size_t i, _VectorArgs *__va) { "; code += "return Create" + vector_type.struct_def->name; - code += "(*__va->__fbb, __va->_" + value + "[i]" + - GenPtrGet(field) + ", "; - code += "__va->__rehasher); }, &_va )"; + code += "(*__va->__fbb, "; + if (field.native_inline) { + code += "&(__va->_" + value + "[i])"; + } else { + code += "__va->_" + value + "[i]" + GenPtrGet(field); + } + code += ", __va->__rehasher); }, &_va )"; } break; } @@ -3342,8 +3348,9 @@ class CppGenerator : public BaseGenerator { // _o->field ? CreateT(_fbb, _o->field.get(), _rehasher); const auto type = field.value.type.struct_def->name; code += value + " ? Create" + type; - code += "(_fbb, " + value + GenPtrGet(field) + ", _rehasher)"; - code += " : 0"; + code += "(_fbb, " + value; + if (!field.native_inline) code += GenPtrGet(field); + code += ", _rehasher) : 0"; } break; } diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 247152fa8..fabe9acb5 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -1153,8 +1153,12 @@ CheckedError Parser::ParseField(StructDef &struct_def) { "definition"); field->native_inline = field->attributes.Lookup("native_inline") != nullptr; - if (field->native_inline && !IsStruct(field->value.type)) - return Error("native_inline can only be defined on structs"); + if (field->native_inline && !IsStruct(field->value.type) && + !IsVectorOfStruct(field->value.type) && + !IsVectorOfTable(field->value.type)) + return Error( + "'native_inline' can only be defined on structs, vector of structs or " + "vector of tables"); auto nested = field->attributes.Lookup("nested_flatbuffer"); if (nested) { diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 71e884cb9..1e8ccc077 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -25,6 +25,7 @@ cc_test( "monster_test_bfbs_generated.h", "namespace_test/namespace_test1_generated.h", "namespace_test/namespace_test2_generated.h", + "native_inline_table_test_generated.h", "native_type_test_impl.cpp", "native_type_test_impl.h", "optional_scalars_generated.h", diff --git a/tests/native_inline_table_test.fbs b/tests/native_inline_table_test.fbs new file mode 100644 index 000000000..cbe8940b5 --- /dev/null +++ b/tests/native_inline_table_test.fbs @@ -0,0 +1,7 @@ +table NativeInlineTable { + a: int; +} + +table TestNativeInlineTable { + t: [NativeInlineTable] (native_inline); +} diff --git a/tests/native_inline_table_test_generated.h b/tests/native_inline_table_test_generated.h new file mode 100644 index 000000000..4f055e610 --- /dev/null +++ b/tests/native_inline_table_test_generated.h @@ -0,0 +1,263 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_NATIVEINLINETABLETEST_H_ +#define FLATBUFFERS_GENERATED_NATIVEINLINETABLETEST_H_ + +#include "flatbuffers/flatbuffers.h" + +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 2 && + FLATBUFFERS_VERSION_MINOR == 0 && + FLATBUFFERS_VERSION_REVISION == 7, + "Non-compatible flatbuffers version included"); + +struct NativeInlineTable; +struct NativeInlineTableBuilder; +struct NativeInlineTableT; + +struct TestNativeInlineTable; +struct TestNativeInlineTableBuilder; +struct TestNativeInlineTableT; + +bool operator==(const NativeInlineTableT &lhs, const NativeInlineTableT &rhs); +bool operator!=(const NativeInlineTableT &lhs, const NativeInlineTableT &rhs); +bool operator==(const TestNativeInlineTableT &lhs, const TestNativeInlineTableT &rhs); +bool operator!=(const TestNativeInlineTableT &lhs, const TestNativeInlineTableT &rhs); + +inline const flatbuffers::TypeTable *NativeInlineTableTypeTable(); + +inline const flatbuffers::TypeTable *TestNativeInlineTableTypeTable(); + +struct NativeInlineTableT : public flatbuffers::NativeTable { + typedef NativeInlineTable TableType; + int32_t a = 0; +}; + +struct NativeInlineTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef NativeInlineTableT NativeTableType; + typedef NativeInlineTableBuilder Builder; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return NativeInlineTableTypeTable(); + } + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4 + }; + int32_t a() const { + return GetField(VT_A, 0); + } + bool mutate_a(int32_t _a = 0) { + return SetField(VT_A, _a, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A, 4) && + verifier.EndTable(); + } + NativeInlineTableT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(NativeInlineTableT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const NativeInlineTableT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct NativeInlineTableBuilder { + typedef NativeInlineTable Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(int32_t a) { + fbb_.AddElement(NativeInlineTable::VT_A, a, 0); + } + explicit NativeInlineTableBuilder(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 CreateNativeInlineTable( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t a = 0) { + NativeInlineTableBuilder builder_(_fbb); + builder_.add_a(a); + return builder_.Finish(); +} + +flatbuffers::Offset CreateNativeInlineTable(flatbuffers::FlatBufferBuilder &_fbb, const NativeInlineTableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct TestNativeInlineTableT : public flatbuffers::NativeTable { + typedef TestNativeInlineTable TableType; + std::vector t{}; +}; + +struct TestNativeInlineTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef TestNativeInlineTableT NativeTableType; + typedef TestNativeInlineTableBuilder Builder; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return TestNativeInlineTableTypeTable(); + } + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_T = 4 + }; + const flatbuffers::Vector> *t() const { + return GetPointer> *>(VT_T); + } + flatbuffers::Vector> *mutable_t() { + return GetPointer> *>(VT_T); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_T) && + verifier.VerifyVector(t()) && + verifier.VerifyVectorOfTables(t()) && + verifier.EndTable(); + } + TestNativeInlineTableT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(TestNativeInlineTableT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const TestNativeInlineTableT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct TestNativeInlineTableBuilder { + typedef TestNativeInlineTable Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_t(flatbuffers::Offset>> t) { + fbb_.AddOffset(TestNativeInlineTable::VT_T, t); + } + explicit TestNativeInlineTableBuilder(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 CreateTestNativeInlineTable( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset>> t = 0) { + TestNativeInlineTableBuilder builder_(_fbb); + builder_.add_t(t); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTestNativeInlineTableDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector> *t = nullptr) { + auto t__ = t ? _fbb.CreateVector>(*t) : 0; + return CreateTestNativeInlineTable( + _fbb, + t__); +} + +flatbuffers::Offset CreateTestNativeInlineTable(flatbuffers::FlatBufferBuilder &_fbb, const TestNativeInlineTableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + + +inline bool operator==(const NativeInlineTableT &lhs, const NativeInlineTableT &rhs) { + return + (lhs.a == rhs.a); +} + +inline bool operator!=(const NativeInlineTableT &lhs, const NativeInlineTableT &rhs) { + return !(lhs == rhs); +} + + +inline NativeInlineTableT *NativeInlineTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::unique_ptr(new NativeInlineTableT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void NativeInlineTable::UnPackTo(NativeInlineTableT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = a(); _o->a = _e; } +} + +inline flatbuffers::Offset NativeInlineTable::Pack(flatbuffers::FlatBufferBuilder &_fbb, const NativeInlineTableT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateNativeInlineTable(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateNativeInlineTable(flatbuffers::FlatBufferBuilder &_fbb, const NativeInlineTableT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const NativeInlineTableT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _a = _o->a; + return CreateNativeInlineTable( + _fbb, + _a); +} + + +inline bool operator==(const TestNativeInlineTableT &lhs, const TestNativeInlineTableT &rhs) { + return + (lhs.t == rhs.t); +} + +inline bool operator!=(const TestNativeInlineTableT &lhs, const TestNativeInlineTableT &rhs) { + return !(lhs == rhs); +} + + +inline TestNativeInlineTableT *TestNativeInlineTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::unique_ptr(new TestNativeInlineTableT()); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void TestNativeInlineTable::UnPackTo(TestNativeInlineTableT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = t(); if (_e) { _o->t.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->t[_i] = *flatbuffers::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } +} + +inline flatbuffers::Offset TestNativeInlineTable::Pack(flatbuffers::FlatBufferBuilder &_fbb, const TestNativeInlineTableT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreateTestNativeInlineTable(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreateTestNativeInlineTable(flatbuffers::FlatBufferBuilder &_fbb, const TestNativeInlineTableT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const TestNativeInlineTableT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _t = _o->t.size() ? _fbb.CreateVector> (_o->t.size(), [](size_t i, _VectorArgs *__va) { return CreateNativeInlineTable(*__va->__fbb, &(__va->__o->t[i]), __va->__rehasher); }, &_va ) : 0; + return CreateTestNativeInlineTable( + _fbb, + _t); +} + +inline const flatbuffers::TypeTable *NativeInlineTableTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_INT, 0, -1 } + }; + static const char * const names[] = { + "a" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, nullptr, names + }; + return &tt; +} + +inline const flatbuffers::TypeTable *TestNativeInlineTableTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 1, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + NativeInlineTableTypeTable + }; + static const char * const names[] = { + "t" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, nullptr, names + }; + return &tt; +} + +#endif // FLATBUFFERS_GENERATED_NATIVEINLINETABLETEST_H_ diff --git a/tests/test.cpp b/tests/test.cpp index 1a46ee7cb..842df9854 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -30,6 +30,7 @@ #include "monster_test.h" #include "monster_test_generated.h" #include "optional_scalars_test.h" +#include "native_inline_table_test_generated.h" #include "parser_test.h" #include "proto_test.h" #include "reflection_test.h" @@ -1362,6 +1363,30 @@ void VectorSpanTest() { } } +void NativeInlineTableVectorTest() { + TestNativeInlineTableT test; + for (int i = 0; i < 10; ++i) { + NativeInlineTableT t; + t.a = i; + test.t.push_back(t); + } + + flatbuffers::FlatBufferBuilder fbb; + auto offset = TestNativeInlineTable::Pack(fbb, &test); + fbb.Finish(offset); + + auto *root = + flatbuffers::GetRoot(fbb.GetBufferPointer()); + TestNativeInlineTableT unpacked; + root->UnPackTo(&unpacked); + + for (int i = 0; i < 10; ++i) { + TEST_ASSERT(unpacked.t[i] == test.t[i]); + } + + TEST_ASSERT(unpacked.t == test.t); +} + int FlatBufferTests(const std::string &tests_data_path) { // Run our various test suites: @@ -1460,6 +1485,7 @@ int FlatBufferTests(const std::string &tests_data_path) { PrivateAnnotationsLeaks(); JsonUnsortedArrayTest(); VectorSpanTest(); + NativeInlineTableVectorTest(); return 0; } } // namespace