From 4f6ad45b0e546fd91cbb5d086661d3166e3e50f7 Mon Sep 17 00:00:00 2001 From: Romain BOULLARD Date: Wed, 29 Jul 2026 22:57:58 +0200 Subject: [PATCH] templated type --- docs/source/languages/cpp.md | 44 +++++++ include/flatbuffers/idl.h | 2 + src/idl_gen_cpp.cpp | 44 +++++-- src/idl_parser.cpp | 100 ++++++++++++++ tests/native_type_test.fbs | 16 +++ tests/native_type_test_generated.h | 202 +++++++++++++++++++++++++++-- tests/native_type_test_impl.cpp | 28 ++++ tests/native_type_test_impl.h | 41 ++++++ tests/parser_test.cpp | 19 +++ tests/test.cpp | 7 + 10 files changed, 478 insertions(+), 25 deletions(-) diff --git a/docs/source/languages/cpp.md b/docs/source/languages/cpp.md index a1769717d..3bd59e5bd 100644 --- a/docs/source/languages/cpp.md +++ b/docs/source/languages/cpp.md @@ -242,6 +242,50 @@ provide the following functions to aide in the serialization process: } ``` +- `native_type_template("type<{{T}}>")` (on a struct) together with + `native_type_template_arg("type")` (on a field of that struct type): + lets a single struct declaration back a native type that is a C++ + template, instantiated differently per field, instead of requiring one + struct declaration (and `native_type`) per instantiation. For example: + +```cpp + struct Box (native_type_template: "Native::Box<{{T}}>") { + value: int32; + } + + table Example { + a: Box (native_inline, native_type_template_arg: "Native::TypeA"); + b: Box (native_inline, native_type_template_arg: "Native::TypeB"); + } +``` + +is equivalent to writing, on each field itself: + +```cpp + a: Box (native_inline, native_type: "Native::Box", native_type_pack_name: "BoxTypeA"); + b: Box (native_inline, native_type: "Native::Box", native_type_pack_name: "BoxTypeB"); +``` + +`native_type_template_arg` also auto-derives a `native_type_pack_name` of +`` (here, `BoxTypeA`/`BoxTypeB`) so the Pack/UnPack +functions stay unique across instantiations without spelling it out +yourself; an explicit `native_type_pack_name` on the field still overrides +this. More than one template parameter is supported by separating them with +commas and using indexed placeholders: + +```cpp + struct Pair (native_type_template: "Native::Pair<{{T0}}, {{T1}}>") { + value: int32; + } + + table Example2 { + p: Pair (native_inline, native_type_template_arg: "Native::Key, Native::Value"); + } +``` + +`native_type_template_arg` is only valid on fields whose type is a struct +(or vector of structs) that declares `native_type_template`. + - `native_type("type")` (on a table): Tables can also be represented with native types. For example, the following schema: diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 4a1126683..181813766 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -1018,6 +1018,8 @@ class Parser : public ParserState { known_attributes_["native_custom_alloc"] = true; known_attributes_["native_type"] = true; known_attributes_["native_type_pack_name"] = true; + known_attributes_["native_type_template"] = true; + known_attributes_["native_type_template_arg"] = true; known_attributes_["native_default"] = true; known_attributes_["flexbuffer"] = true; known_attributes_["private"] = true; diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 6fc820c06..8d1b26be8 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -892,6 +892,24 @@ class CppGenerator : public BaseGenerator { return opts_.gen_nullable ? " _Nullable " : ""; } + // A field's `native_type`/`native_type_pack_name` normally come from the + // referenced struct's own declaration. A field can override both (see + // `native_type_template_arg` in idl_parser.cpp, which synthesizes these + // two attributes directly on the field) to instantiate a templated + // native type differently per field. + static const Value* EffectiveNativeType(const FieldDef& field, + const StructDef& struct_def) { + if (const auto v = field.attributes.Lookup("native_type")) return v; + return struct_def.attributes.Lookup("native_type"); + } + + static const Value* EffectiveNativeTypePackName(const FieldDef& field, + const StructDef& struct_def) { + if (const auto v = field.attributes.Lookup("native_type_pack_name")) + return v; + return struct_def.attributes.Lookup("native_type_pack_name"); + } + 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. @@ -1005,7 +1023,7 @@ class CppGenerator : public BaseGenerator { case BASE_TYPE_STRUCT: { auto type_name = WrapInNameSpace(*type.struct_def); if (IsStruct(type)) { - auto native_type = type.struct_def->attributes.Lookup("native_type"); + auto native_type = EffectiveNativeType(field, *type.struct_def); if (native_type) { type_name = native_type->constant; } @@ -3533,11 +3551,11 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_STRUCT: { if (IsStruct(type)) { - const auto& struct_attrs = type.struct_def->attributes; - const auto native_type = struct_attrs.Lookup("native_type"); + const auto native_type = EffectiveNativeType(afield, *type.struct_def); if (native_type) { std::string unpack_call = "::flatbuffers::UnPack"; - const auto pack_name = struct_attrs.Lookup("native_type_pack_name"); + const auto pack_name = + EffectiveNativeTypePackName(afield, *type.struct_def); if (pack_name) { unpack_call += pack_name->constant; } @@ -3820,9 +3838,8 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_STRUCT: { if (IsStruct(vector_type)) { - const auto& struct_attrs = - field.value.type.struct_def->attributes; - const auto native_type = struct_attrs.Lookup("native_type"); + const auto native_type = + EffectiveNativeType(field, *field.value.type.struct_def); if (native_type) { code += "_fbb.CreateVectorOfNativeStructs<"; code += WrapInNameSpace(*vector_type.struct_def) + ", " + @@ -3832,8 +3849,8 @@ class CppGenerator : public BaseGenerator { } else { code += "(" + value; } - const auto pack_name = - struct_attrs.Lookup("native_type_pack_name"); + const auto pack_name = EffectiveNativeTypePackName( + field, *field.value.type.struct_def); if (pack_name) { code += ", ::flatbuffers::Pack" + pack_name->constant; } @@ -3999,12 +4016,12 @@ class CppGenerator : public BaseGenerator { } case BASE_TYPE_STRUCT: { if (IsStruct(field.value.type)) { - const auto& struct_attribs = field.value.type.struct_def->attributes; - const auto native_type = struct_attribs.Lookup("native_type"); + const auto native_type = + EffectiveNativeType(field, *field.value.type.struct_def); if (native_type && field.native_inline) { code += "::flatbuffers::Pack"; const auto pack_name = - struct_attribs.Lookup("native_type_pack_name"); + EffectiveNativeTypePackName(field, *field.value.type.struct_def); if (pack_name) { code += pack_name->constant; } @@ -4169,8 +4186,7 @@ class CppGenerator : public BaseGenerator { 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"); + EffectiveNativeType(*field, *field->value.type.struct_def); auto native_inline = field->attributes.Lookup("native_inline"); if (native_type) { pass_by_address = true; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 569c76762..0a82156db 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -1286,6 +1286,106 @@ CheckedError Parser::ParseField(StructDef& struct_def) { "'native_inline' can only be defined on structs, vector of structs or " "vector of tables"); + auto native_type_template_arg = + field->attributes.Lookup("native_type_template_arg"); + if (native_type_template_arg) { + if (!IsStruct(field->value.type) && !IsVectorOfStruct(field->value.type)) + return Error( + "'native_type_template_arg' can only be defined on struct-typed " + "fields or vectors of structs"); + const auto* target_struct = field->value.type.struct_def; + const auto* native_type_template = + target_struct->attributes.Lookup("native_type_template"); + if (!native_type_template) + return Error( + "'native_type_template_arg' requires the field's type ('" + + target_struct->name + + "') to declare a 'native_type_template' attribute"); + if (field->attributes.Lookup("native_type")) + return Error( + "'native_type_template_arg' cannot be combined with an explicit " + "'native_type' on the same field"); + + // Split on top-level commas, so multiple template arguments can be + // given (e.g. "::Foo::Key, ::Foo::Value"). Commas nested inside a + // template argument that is itself templated (e.g. + // "std::pair") are not treated as separators. + std::vector args; + { + const auto& s = native_type_template_arg->constant; + int depth = 0; + size_t start = 0; + for (size_t i = 0; i < s.size(); ++i) { + if (s[i] == '<') { + ++depth; + } else if (s[i] == '>') { + --depth; + } else if (s[i] == ',' && depth == 0) { + args.push_back(s.substr(start, i - start)); + start = i + 1; + } + } + args.push_back(s.substr(start)); + for (auto& arg : args) { + const auto b = arg.find_first_not_of(" \t"); + const auto e = arg.find_last_not_of(" \t"); + arg = b == std::string::npos ? "" : arg.substr(b, e - b + 1); + if (arg.empty()) + return Error( + "'native_type_template_arg' contains an empty template " + "argument"); + } + } + + // Substitute each argument for its placeholder: `{{T0}}`, `{{T1}}`, etc. + // For a single argument, the unindexed `{{T}}` spelling is also + // accepted (equivalent to `{{T0}}`). + std::string native_type_str = native_type_template->constant; + for (size_t i = 0; i < args.size(); ++i) { + std::vector placeholders = { "{{T" + NumToString(i) + + "}}" }; + if (i == 0) placeholders.push_back("{{T}}"); + size_t replaced = 0; + for (const auto& placeholder : placeholders) { + for (;;) { + const auto pos = native_type_str.find(placeholder); + if (pos == std::string::npos) break; + native_type_str.replace(pos, placeholder.length(), args[i]); + ++replaced; + } + } + if (!replaced) + return Error("'native_type_template' on '" + target_struct->name + + "' does not reference the '{{T" + NumToString(i) + + "}}' placeholder required for template argument " + + NumToString(i + 1) + " ('" + args[i] + "')"); + } + if (native_type_str.find("{{T") != std::string::npos) + return Error( + "'native_type_template' on '" + target_struct->name + + "' references a '{{T}}' placeholder beyond the " + + NumToString(args.size()) + + " template argument(s) given in 'native_type_template_arg'"); + + auto native_type_val = new Value(); + native_type_val->type = native_type_template_arg->type; + native_type_val->constant = native_type_str; + field->attributes.Add("native_type", native_type_val); + + if (!field->attributes.Lookup("native_type_pack_name")) { + std::string pack_name = target_struct->name; + for (const auto& arg : args) { + const auto last_colon = arg.find_last_of(':'); + pack_name += + last_colon == std::string::npos ? arg : arg.substr(last_colon + 1); + } + auto pack_name_val = new Value(); + pack_name_val->type = native_type_template_arg->type; + pack_name_val->constant = pack_name; + field->attributes.Add("native_type_pack_name", pack_name_val); + } + } + auto nested = field->attributes.Lookup("nested_flatbuffer"); if (nested) { if (nested->type.base_type != BASE_TYPE_STRING) diff --git a/tests/native_type_test.fbs b/tests/native_type_test.fbs index 1e73d3330..b8d84b876 100644 --- a/tests/native_type_test.fbs +++ b/tests/native_type_test.fbs @@ -20,6 +20,19 @@ table Matrix (native_type:"Native::Matrix") { values:[float]; } +// A struct whose native type is a template, instantiated differently per +// field via `native_type_template_arg` instead of needing one flatbuffers +// struct declaration per specialization (compare to Vector3D/Vector3DAlt +// above). +struct Tagged (native_type_template: "Native::Tagged<{{T}}>") { + value:int32; +} + +// A native type template that takes more than one argument. +struct Pair (native_type_template: "Native::Pair<{{T0}}, {{T1}}>") { + value:int32; +} + table ApplicationData { vectors:[Vector3D]; vectors_alt:[Vector3DAlt]; @@ -27,6 +40,9 @@ table ApplicationData { position_inline:Vector3D (native_inline); matrix:Matrix; matrices:[Matrix]; + tagged_a:Tagged (native_inline, native_type_template_arg: "Native::TagA"); + tagged_b:Tagged (native_inline, native_type_template_arg: "Native::TagB"); + pair_ab:Pair (native_inline, native_type_template_arg: "Native::TagA, Native::TagB"); } root_type ApplicationData; diff --git a/tests/native_type_test_generated.h b/tests/native_type_test_generated.h index a401e7264..cfdccb89f 100644 --- a/tests/native_type_test_generated.h +++ b/tests/native_type_test_generated.h @@ -24,10 +24,18 @@ struct Vector3DAlt; struct Matrix; struct MatrixBuilder; +struct Tagged; + +struct Pair; + struct ApplicationData; struct ApplicationDataBuilder; struct ApplicationDataT; +bool operator==(const Tagged &lhs, const Tagged &rhs); +bool operator!=(const Tagged &lhs, const Tagged &rhs); +bool operator==(const Pair &lhs, const Pair &rhs); +bool operator!=(const Pair &lhs, const Pair &rhs); bool operator==(const ApplicationDataT &lhs, const ApplicationDataT &rhs); bool operator!=(const ApplicationDataT &lhs, const ApplicationDataT &rhs); @@ -37,6 +45,10 @@ inline const ::flatbuffers::TypeTable *Vector3DAltTypeTable(); inline const ::flatbuffers::TypeTable *MatrixTypeTable(); +inline const ::flatbuffers::TypeTable *TaggedTypeTable(); + +inline const ::flatbuffers::TypeTable *PairTypeTable(); + inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable(); FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Vector3D FLATBUFFERS_FINAL_CLASS { @@ -121,6 +133,72 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Vector3DAlt FLATBUFFERS_FINAL_CLASS { }; FLATBUFFERS_STRUCT_END(Vector3DAlt, 12); +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Tagged FLATBUFFERS_FINAL_CLASS { + private: + int32_t value_; + + public: + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { + return TaggedTypeTable(); + } + Tagged() + : value_(0) { + } + Tagged(int32_t _value) + : value_(::flatbuffers::EndianScalar(_value)) { + } + int32_t value() const { + return ::flatbuffers::EndianScalar(value_); + } + void mutate_value(int32_t _value) { + ::flatbuffers::WriteScalar(&value_, _value); + } +}; +FLATBUFFERS_STRUCT_END(Tagged, 4); + +inline bool operator==(const Tagged &lhs, const Tagged &rhs) { + return + (lhs.value() == rhs.value()); +} + +inline bool operator!=(const Tagged &lhs, const Tagged &rhs) { + return !(lhs == rhs); +} + + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Pair FLATBUFFERS_FINAL_CLASS { + private: + int32_t value_; + + public: + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { + return PairTypeTable(); + } + Pair() + : value_(0) { + } + Pair(int32_t _value) + : value_(::flatbuffers::EndianScalar(_value)) { + } + int32_t value() const { + return ::flatbuffers::EndianScalar(value_); + } + void mutate_value(int32_t _value) { + ::flatbuffers::WriteScalar(&value_, _value); + } +}; +FLATBUFFERS_STRUCT_END(Pair, 4); + +inline bool operator==(const Pair &lhs, const Pair &rhs) { + return + (lhs.value() == rhs.value()); +} + +inline bool operator!=(const Pair &lhs, const Pair &rhs) { + return !(lhs == rhs); +} + + struct Matrix FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef Native::Matrix NativeTableType; typedef MatrixBuilder Builder; @@ -223,6 +301,9 @@ struct ApplicationDataT : public ::flatbuffers::NativeTable { Native::Vector3D position_inline{}; std::unique_ptr matrix{}; std::vector> matrices{}; + Native::Tagged tagged_a{}; + Native::Tagged tagged_b{}; + Native::Pair pair_ab{}; ApplicationDataT() = default; ApplicationDataT(const ApplicationDataT &o); ApplicationDataT(ApplicationDataT&&) FLATBUFFERS_NOEXCEPT = default; @@ -241,7 +322,10 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VT_POSITION = 8, VT_POSITION_INLINE = 10, VT_MATRIX = 12, - VT_MATRICES = 14 + VT_MATRICES = 14, + VT_TAGGED_A = 16, + VT_TAGGED_B = 18, + VT_PAIR_AB = 20 }; const ::flatbuffers::Vector *vectors() const { return GetPointer *>(VT_VECTORS); @@ -279,6 +363,24 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { ::flatbuffers::Vector<::flatbuffers::Offset> *mutable_matrices() { return GetPointer<::flatbuffers::Vector<::flatbuffers::Offset> *>(VT_MATRICES); } + const Geometry::Tagged *tagged_a() const { + return GetStruct(VT_TAGGED_A); + } + Geometry::Tagged *mutable_tagged_a() { + return GetStruct(VT_TAGGED_A); + } + const Geometry::Tagged *tagged_b() const { + return GetStruct(VT_TAGGED_B); + } + Geometry::Tagged *mutable_tagged_b() { + return GetStruct(VT_TAGGED_B); + } + const Geometry::Pair *pair_ab() const { + return GetStruct(VT_PAIR_AB); + } + Geometry::Pair *mutable_pair_ab() { + return GetStruct(VT_PAIR_AB); + } template bool Verify(::flatbuffers::VerifierTemplate &verifier) const { return VerifyTableStart(verifier) && @@ -293,6 +395,9 @@ struct ApplicationData FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VerifyOffset(verifier, VT_MATRICES) && verifier.VerifyVector(matrices()) && verifier.VerifyVectorOfTables(matrices()) && + VerifyField(verifier, VT_TAGGED_A, 4) && + VerifyField(verifier, VT_TAGGED_B, 4) && + VerifyField(verifier, VT_PAIR_AB, 4) && verifier.EndTable(); } ApplicationDataT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -322,6 +427,15 @@ struct ApplicationDataBuilder { void add_matrices(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> matrices) { fbb_.AddOffset(ApplicationData::VT_MATRICES, matrices); } + void add_tagged_a(const Geometry::Tagged *tagged_a) { + fbb_.AddStruct(ApplicationData::VT_TAGGED_A, tagged_a); + } + void add_tagged_b(const Geometry::Tagged *tagged_b) { + fbb_.AddStruct(ApplicationData::VT_TAGGED_B, tagged_b); + } + void add_pair_ab(const Geometry::Pair *pair_ab) { + fbb_.AddStruct(ApplicationData::VT_PAIR_AB, pair_ab); + } explicit ApplicationDataBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -340,8 +454,14 @@ inline ::flatbuffers::Offset CreateApplicationData( const Geometry::Vector3D *position = nullptr, const Geometry::Vector3D *position_inline = nullptr, ::flatbuffers::Offset matrix = 0, - ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> matrices = 0) { + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> matrices = 0, + const Geometry::Tagged *tagged_a = nullptr, + const Geometry::Tagged *tagged_b = nullptr, + const Geometry::Pair *pair_ab = nullptr) { ApplicationDataBuilder builder_(_fbb); + builder_.add_pair_ab(pair_ab); + builder_.add_tagged_b(tagged_b); + builder_.add_tagged_a(tagged_a); builder_.add_matrices(matrices); builder_.add_matrix(matrix); builder_.add_position_inline(position_inline); @@ -358,7 +478,10 @@ inline ::flatbuffers::Offset CreateApplicationDataDirect( const Geometry::Vector3D *position = nullptr, const Geometry::Vector3D *position_inline = nullptr, ::flatbuffers::Offset matrix = 0, - const std::vector<::flatbuffers::Offset> *matrices = nullptr) { + const std::vector<::flatbuffers::Offset> *matrices = nullptr, + const Geometry::Tagged *tagged_a = nullptr, + const Geometry::Tagged *tagged_b = nullptr, + const Geometry::Pair *pair_ab = 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; @@ -369,7 +492,10 @@ inline ::flatbuffers::Offset CreateApplicationDataDirect( position, position_inline, matrix, - matrices__); + matrices__, + tagged_a, + tagged_b, + pair_ab); } ::flatbuffers::Offset CreateApplicationData(::flatbuffers::FlatBufferBuilder &_fbb, const ApplicationDataT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -392,7 +518,10 @@ inline bool operator==(const ApplicationDataT &lhs, const ApplicationDataT &rhs) ((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); })); + (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); })) && + (lhs.tagged_a == rhs.tagged_a) && + (lhs.tagged_b == rhs.tagged_b) && + (lhs.pair_ab == rhs.pair_ab); } inline bool operator!=(const ApplicationDataT &lhs, const ApplicationDataT &rhs) { @@ -405,7 +534,10 @@ inline ApplicationDataT::ApplicationDataT(const ApplicationDataT &o) vectors_alt(o.vectors_alt), position((o.position) ? new Native::Vector3D(*o.position) : nullptr), position_inline(o.position_inline), - matrix((o.matrix) ? new Native::Matrix(*o.matrix) : nullptr) { + matrix((o.matrix) ? new Native::Matrix(*o.matrix) : nullptr), + tagged_a(o.tagged_a), + tagged_b(o.tagged_b), + pair_ab(o.pair_ab) { matrices.reserve(o.matrices.size()); for (const auto &matrices_ : o.matrices) { matrices.emplace_back((matrices_) ? new Native::Matrix(*matrices_) : nullptr); } } @@ -417,6 +549,9 @@ inline ApplicationDataT &ApplicationDataT::operator=(ApplicationDataT o) FLATBUF std::swap(position_inline, o.position_inline); std::swap(matrix, o.matrix); std::swap(matrices, o.matrices); + std::swap(tagged_a, o.tagged_a); + std::swap(tagged_b, o.tagged_b); + std::swap(pair_ab, o.pair_ab); return *this; } @@ -435,6 +570,9 @@ inline void ApplicationData::UnPackTo(ApplicationDataT *_o, const ::flatbuffers: { 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); } } + { auto _e = tagged_a(); if (_e) _o->tagged_a = ::flatbuffers::UnPackTaggedTagA(*_e); } + { auto _e = tagged_b(); if (_e) _o->tagged_b = ::flatbuffers::UnPackTaggedTagB(*_e); } + { auto _e = pair_ab(); if (_e) _o->pair_ab = ::flatbuffers::UnPackPairTagATagB(*_e); } } inline ::flatbuffers::Offset CreateApplicationData(::flatbuffers::FlatBufferBuilder &_fbb, const ApplicationDataT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) { @@ -451,6 +589,9 @@ inline ::flatbuffers::Offset ApplicationData::Pack(::flatbuffer 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; + auto _tagged_a = ::flatbuffers::PackTaggedTagA(_o->tagged_a); + auto _tagged_b = ::flatbuffers::PackTaggedTagB(_o->tagged_b); + auto _pair_ab = ::flatbuffers::PackPairTagATagB(_o->pair_ab); return Geometry::CreateApplicationData( _fbb, _vectors, @@ -458,7 +599,10 @@ inline ::flatbuffers::Offset ApplicationData::Pack(::flatbuffer _o->position ? &_position : nullptr, &_position_inline, _matrix, - _matrices); + _matrices, + &_tagged_a, + &_tagged_b, + &_pair_ab); } inline const ::flatbuffers::TypeTable *Vector3DTypeTable() { @@ -514,6 +658,34 @@ inline const ::flatbuffers::TypeTable *MatrixTypeTable() { return &tt; } +inline const ::flatbuffers::TypeTable *TaggedTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 } + }; + static const int64_t values[] = { 0, 4 }; + static const char * const names[] = { + "value" + }; + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names + }; + return &tt; +} + +inline const ::flatbuffers::TypeTable *PairTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 } + }; + static const int64_t values[] = { 0, 4 }; + static const char * const names[] = { + "value" + }; + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names + }; + return &tt; +} + inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable() { static const ::flatbuffers::TypeCode type_codes[] = { { ::flatbuffers::ET_SEQUENCE, 1, 0 }, @@ -521,12 +693,17 @@ inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable() { { ::flatbuffers::ET_SEQUENCE, 0, 0 }, { ::flatbuffers::ET_SEQUENCE, 0, 0 }, { ::flatbuffers::ET_SEQUENCE, 0, 2 }, - { ::flatbuffers::ET_SEQUENCE, 1, 2 } + { ::flatbuffers::ET_SEQUENCE, 1, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 3 }, + { ::flatbuffers::ET_SEQUENCE, 0, 3 }, + { ::flatbuffers::ET_SEQUENCE, 0, 4 } }; static const ::flatbuffers::TypeFunction type_refs[] = { Geometry::Vector3DTypeTable, Geometry::Vector3DAltTypeTable, - Geometry::MatrixTypeTable + Geometry::MatrixTypeTable, + Geometry::TaggedTypeTable, + Geometry::PairTypeTable }; static const char * const names[] = { "vectors", @@ -534,10 +711,13 @@ inline const ::flatbuffers::TypeTable *ApplicationDataTypeTable() { "position", "position_inline", "matrix", - "matrices" + "matrices", + "tagged_a", + "tagged_b", + "pair_ab" }; static const ::flatbuffers::TypeTable tt = { - ::flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, nullptr, names + ::flatbuffers::ST_TABLE, 9, 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 8719c140f..c2bb31942 100644 --- a/tests/native_type_test_impl.cpp +++ b/tests/native_type_test_impl.cpp @@ -18,6 +18,34 @@ Geometry::Vector3DAlt PackVector3DAlt(const Native::Vector3D& obj) { const Native::Vector3D UnPackVector3DAlt(const Geometry::Vector3DAlt& obj) { return Native::Vector3D(obj.a(), obj.b(), obj.c()); } + +Geometry::Tagged PackTaggedTagA(const Native::Tagged& obj) { + return Geometry::Tagged(obj.value); +} + +const Native::Tagged UnPackTaggedTagA( + const Geometry::Tagged& obj) { + return Native::Tagged(obj.value()); +} + +Geometry::Tagged PackTaggedTagB(const Native::Tagged& obj) { + return Geometry::Tagged(obj.value); +} + +const Native::Tagged UnPackTaggedTagB( + const Geometry::Tagged& obj) { + return Native::Tagged(obj.value()); +} + +Geometry::Pair PackPairTagATagB( + const Native::Pair& obj) { + return Geometry::Pair(obj.value); +} + +const Native::Pair UnPackPairTagATagB( + const Geometry::Pair& obj) { + return Native::Pair(obj.value()); +} } // namespace flatbuffers namespace Geometry { diff --git a/tests/native_type_test_impl.h b/tests/native_type_test_impl.h index 88781f9fe..94c799cec 100644 --- a/tests/native_type_test_impl.h +++ b/tests/native_type_test_impl.h @@ -43,11 +43,42 @@ struct Matrix { (values == other.values); } }; + +// Phantom tag types used purely to select a template specialization; they +// carry no data of their own. +struct TagA {}; +struct TagB {}; + +// A native type template, instantiated per-field in the schema via +// `native_type_template_arg` rather than needing one flatbuffers struct +// declaration per specialization. +template +struct Tagged { + int32_t value; + + Tagged() : value(0) {} + explicit Tagged(int32_t _value) : value(_value) {} + + bool operator==(const Tagged& other) const { return value == other.value; } +}; + +// A native type template taking more than one argument. +template +struct Pair { + int32_t value; + + Pair() : value(0) {} + explicit Pair(int32_t _value) : value(_value) {} + + bool operator==(const Pair& other) const { return value == other.value; } +}; } // namespace Native namespace Geometry { struct Vector3D; struct Vector3DAlt; +struct Tagged; +struct Pair; } // namespace Geometry namespace flatbuffers { @@ -55,6 +86,16 @@ Geometry::Vector3D Pack(const Native::Vector3D& obj); const Native::Vector3D UnPack(const Geometry::Vector3D& obj); Geometry::Vector3DAlt PackVector3DAlt(const Native::Vector3D& obj); const Native::Vector3D UnPackVector3DAlt(const Geometry::Vector3DAlt& obj); + +Geometry::Tagged PackTaggedTagA(const Native::Tagged& obj); +const Native::Tagged UnPackTaggedTagA(const Geometry::Tagged& obj); +Geometry::Tagged PackTaggedTagB(const Native::Tagged& obj); +const Native::Tagged UnPackTaggedTagB(const Geometry::Tagged& obj); + +Geometry::Pair PackPairTagATagB( + const Native::Pair& obj); +const Native::Pair UnPackPairTagATagB( + const Geometry::Pair& obj); } // namespace flatbuffers #endif // VECTOR3D_PACK_H diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp index e03fd9623..0c8260e93 100644 --- a/tests/parser_test.cpp +++ b/tests/parser_test.cpp @@ -97,6 +97,25 @@ void ErrorTest() { "datatype already"); TestError("struct X (force_align: 7) { Y:int; }", "force_align"); TestError("struct X {}", "size 0"); + TestError( + "struct X { Y:int; } table T { y:X (native_type_template_arg:\"int\"); " + "}", + "'native_type_template' attribute"); + TestError( + "table T { y:int (native_type_template_arg:\"int\"); }", + "struct-typed fields"); + TestError( + "struct X (native_type_template: \"Foo\") { Y:int; } " + "table T { y:X (native_type_template_arg:\"int\"); }", + "does not reference"); + TestError( + "struct X (native_type_template: \"Foo<{{T0}}, {{T1}}>\") { Y:int; } " + "table T { y:X (native_type_template_arg:\"int\"); }", + "beyond the"); + TestError( + "struct X (native_type_template: \"Foo<{{T}}>\") { Y:int; } " + "table T { y:X (native_type_template_arg:\"\"); }", + "empty template argument"); TestError("{}", "no root"); TestError("table X { Y:byte; } root_type X; { Y:1 } { Y:1 }", "end of file"); TestError("table X { Y:byte; } root_type X; { Y:1 } table Y{ Z:int }", diff --git a/tests/test.cpp b/tests/test.cpp index 4cf2e9381..c7fe57d4f 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -930,6 +930,10 @@ void NativeTypeTest() { Native::Vector3D(20 * i + 0.1f, 20 * i + 0.2f, 20 * i + 0.3f)); } + src_data.tagged_a = Native::Tagged(7); + src_data.tagged_b = Native::Tagged(8); + src_data.pair_ab = Native::Pair(9); + src_data.matrix = std::unique_ptr(new Native::Matrix(1, 2)); src_data.matrix->values = {3, 4}; @@ -951,6 +955,9 @@ void NativeTypeTest() { TEST_EQ(dstDataT->position_inline.x, 4.0f); TEST_EQ(dstDataT->position_inline.y, 5.0f); TEST_EQ(dstDataT->position_inline.z, 6.0f); + TEST_EQ(dstDataT->tagged_a.value, 7); + TEST_EQ(dstDataT->tagged_b.value, 8); + TEST_EQ(dstDataT->pair_ab.value, 9); for (int i = 0; i < N; ++i) { const Native::Vector3D& v = dstDataT->vectors[i];