diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md index 2ded61103..e858e0a34 100644 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -126,6 +126,45 @@ The following attributes are specific to the object-based API code generation: "native_inline", the value specified with this attribute will be included verbatim in the class constructor initializer list for this member. +- `native_custom_alloc`:"custom_allocator" (on a table or struct): When using the + object-based API all generated NativeTables that are allocated when unpacking + your flatbuffer will use "custom allocator". The allocator is also used by + any std::vector that appears in a table defined with `native_custom_alloc`. + This can be used to provide allocation from a pool for example, for faster + unpacking when using the object-based API. + + Minimal Example: + + schema: + + table mytable(native_custom_alloc:"custom_allocator") { + ... + } + + with custom_allocator defined before flatbuffers.h is included, as: + + template struct custom_allocator : public std::allocator { + + typedef T *pointer; + + template + struct rebind { + typedef custom_allocator other; + }; + + pointer allocate(const std::size_t n) { + return std::allocator::allocate(n); + } + + void deallocate(T* ptr, std::size_t n) { + return std::allocator::deallocate(ptr,n); + } + + custom_allocator() throw() {} + template + custom_allocator(const custom_allocator&) throw() {} + }; + - `native_type`' "type" (on a struct): In some cases, a more optimal C++ data type exists for a given struct. For example, the following schema: diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 3b56ba491..aa5df859f 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -656,10 +656,10 @@ inline voffset_t FieldIndexToOffset(voffset_t field_id) { return static_cast((field_id + fixed_fields) * sizeof(voffset_t)); } -template const T* data(const std::vector &v) { +template const T* data(const std::vector &v) { return v.empty() ? nullptr : &v.front(); } -template T* data(std::vector &v) { +template T* data(std::vector &v) { return v.empty() ? nullptr : &v.front(); } @@ -1283,8 +1283,8 @@ class FlatBufferBuilder /// serialize into the buffer as a `vector`. /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. - template Offset> CreateVectorOfStructs( - const std::vector &v) { + template Offset> CreateVectorOfStructs( + const std::vector &v) { return CreateVectorOfStructs(data(v), v.size()); } diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index c0408de63..d06b5a429 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -526,6 +526,7 @@ class Parser : public ParserState { known_attributes_["cpp_ptr_type"] = true; known_attributes_["cpp_str_type"] = true; known_attributes_["native_inline"] = true; + known_attributes_["native_custom_alloc"] = true; known_attributes_["native_type"] = true; known_attributes_["native_default"] = true; known_attributes_["flexbuffer"] = true; diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 623ada2f1..8b2837953 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -479,7 +479,11 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_VECTOR: { const auto type_name = GenTypeNative(type.VectorType(), true, field); - return "std::vector<" + type_name + ">"; + if (type.struct_def && type.struct_def->attributes.Lookup("native_custom_alloc")) { + auto native_custom_alloc = type.struct_def->attributes.Lookup("native_custom_alloc"); + return "std::vector<" + type_name + "," + native_custom_alloc->constant + "<" + type_name + ">>"; + } else + return "std::vector<" + type_name + ">"; } case BASE_TYPE_STRUCT: { auto type_name = WrapInNameSpace(*type.struct_def); @@ -1323,6 +1327,17 @@ class CppGenerator : public BaseGenerator { code_ += " }"; } + void GenOperatorNewDelete(const StructDef & struct_def) { + if (auto native_custom_alloc = struct_def.attributes.Lookup("native_custom_alloc")) { + code_ += " inline void *operator new (std::size_t count) {"; + code_ += " return " + native_custom_alloc->constant + "<{{NATIVE_NAME}}>().allocate(count / sizeof({{NATIVE_NAME}}));"; + code_ += " }"; + code_ += " inline void operator delete (void *ptr) {"; + code_ += " return " + native_custom_alloc->constant + "<{{NATIVE_NAME}}>().deallocate(static_cast<{{NATIVE_NAME}}*>(ptr),1);"; + code_ += " }"; + } + } + void GenNativeTable(const StructDef &struct_def) { const auto native_name = NativeName(Name(struct_def), &struct_def, parser_.opts); code_.SetValue("STRUCT_NAME", Name(struct_def)); @@ -1336,6 +1351,7 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { GenMember(**it); } + GenOperatorNewDelete(struct_def); GenDefaultConstructor(struct_def); code_ += "};"; code_ += ""; @@ -2388,6 +2404,8 @@ class CppGenerator : public BaseGenerator { code_ += " }"; } } + code_.SetValue("NATIVE_NAME", Name(struct_def)); + GenOperatorNewDelete(struct_def); code_ += "};"; code_.SetValue("STRUCT_BYTE_SIZE", NumToString(struct_def.bytesize)); diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index cc58a1cda..65e7cb86a 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -676,6 +676,10 @@ CheckedError Parser::ParseField(StructDef &struct_def) { } } + auto field_native_custom_alloc = field->attributes.Lookup("native_custom_alloc"); + if (field_native_custom_alloc) + return Error("native_custom_alloc can only be used with a table or struct 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'"); diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs index 843e308c5..2e247eb3a 100644 Binary files a/tests/monster_test.bfbs and b/tests/monster_test.bfbs differ diff --git a/tests/namespace_test/namespace_test2_generated.ts b/tests/namespace_test/namespace_test2_generated.ts index 8deae09c4..8a4aebd05 100644 --- a/tests/namespace_test/namespace_test2_generated.ts +++ b/tests/namespace_test/namespace_test2_generated.ts @@ -1,6 +1,6 @@ // automatically generated by the FlatBuffers compiler, do not modify -import * as NS9459827973991502386 from "./namespace_test1_generated"; +import * as NS11563891686210618450 from "./namespace_test1_generated"; /** * @constructor */ @@ -39,24 +39,24 @@ static getRootAsTableInFirstNS(bb:flatbuffers.ByteBuffer, obj?:TableInFirstNS):T * @param {NamespaceA.NamespaceB.TableInNestedNS=} obj * @returns {NamespaceA.NamespaceB.TableInNestedNS|null} */ -fooTable(obj?:NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS):NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS|null { +fooTable(obj?:NS11563891686210618450.NamespaceA.NamespaceB.TableInNestedNS):NS11563891686210618450.NamespaceA.NamespaceB.TableInNestedNS|null { var offset = this.bb.__offset(this.bb_pos, 4); - return offset ? (obj || new NS9459827973991502386.NamespaceA.NamespaceB.TableInNestedNS).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null; + return offset ? (obj || new NS11563891686210618450.NamespaceA.NamespaceB.TableInNestedNS).__init(this.bb.__indirect(this.bb_pos + offset), this.bb) : null; }; /** * @returns {NamespaceA.NamespaceB.EnumInNestedNS} */ -fooEnum():NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS { +fooEnum():NS11563891686210618450.NamespaceA.NamespaceB.EnumInNestedNS { var offset = this.bb.__offset(this.bb_pos, 6); - return offset ? /** @type {NamespaceA.NamespaceB.EnumInNestedNS} */ (this.bb.readInt8(this.bb_pos + offset)) : NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS.A; + return offset ? /** @type {NamespaceA.NamespaceB.EnumInNestedNS} */ (this.bb.readInt8(this.bb_pos + offset)) : NS11563891686210618450.NamespaceA.NamespaceB.EnumInNestedNS.A; }; /** * @param {NamespaceA.NamespaceB.EnumInNestedNS} value * @returns {boolean} */ -mutate_foo_enum(value:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS):boolean { +mutate_foo_enum(value:NS11563891686210618450.NamespaceA.NamespaceB.EnumInNestedNS):boolean { var offset = this.bb.__offset(this.bb_pos, 6); if (offset === 0) { @@ -71,9 +71,9 @@ mutate_foo_enum(value:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS * @param {NamespaceA.NamespaceB.StructInNestedNS=} obj * @returns {NamespaceA.NamespaceB.StructInNestedNS|null} */ -fooStruct(obj?:NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS):NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS|null { +fooStruct(obj?:NS11563891686210618450.NamespaceA.NamespaceB.StructInNestedNS):NS11563891686210618450.NamespaceA.NamespaceB.StructInNestedNS|null { var offset = this.bb.__offset(this.bb_pos, 8); - return offset ? (obj || new NS9459827973991502386.NamespaceA.NamespaceB.StructInNestedNS).__init(this.bb_pos + offset, this.bb) : null; + return offset ? (obj || new NS11563891686210618450.NamespaceA.NamespaceB.StructInNestedNS).__init(this.bb_pos + offset, this.bb) : null; }; /** @@ -95,8 +95,8 @@ static addFooTable(builder:flatbuffers.Builder, fooTableOffset:flatbuffers.Offse * @param {flatbuffers.Builder} builder * @param {NamespaceA.NamespaceB.EnumInNestedNS} fooEnum */ -static addFooEnum(builder:flatbuffers.Builder, fooEnum:NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS) { - builder.addFieldInt8(1, fooEnum, NS9459827973991502386.NamespaceA.NamespaceB.EnumInNestedNS.A); +static addFooEnum(builder:flatbuffers.Builder, fooEnum:NS11563891686210618450.NamespaceA.NamespaceB.EnumInNestedNS) { + builder.addFieldInt8(1, fooEnum, NS11563891686210618450.NamespaceA.NamespaceB.EnumInNestedNS.A); }; /**