Simplify templates

This commit is contained in:
2026-07-29 23:11:48 +02:00
parent 4f6ad45b0e
commit a06c41f2c7
4 changed files with 21 additions and 42 deletions
+11 -5
View File
@@ -242,14 +242,19 @@ provide the following functions to aide in the serialization process:
} }
``` ```
- `native_type_template("type<{{T}}>")` (on a struct) together with - `native_type_template("type")` (on a struct) together with
`native_type_template_arg("type")` (on a field of that struct type): `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++ lets a single struct declaration back a native type that is a C++
template, instantiated differently per field, instead of requiring one template, instantiated differently per field, instead of requiring one
struct declaration (and `native_type`) per instantiation. For example: struct declaration (and `native_type`) per instantiation.
`native_type_template_arg` is angle-bracket-appended to
`native_type_template` to form the field's native type, so
`native_type_template: "Native::Box"` with
`native_type_template_arg: "Native::TypeA"` produces
`Native::Box<Native::TypeA>`. For example:
```cpp ```cpp
struct Box (native_type_template: "Native::Box<{{T}}>") { struct Box (native_type_template: "Native::Box") {
value: int32; value: int32;
} }
@@ -271,10 +276,10 @@ is equivalent to writing, on each field itself:
functions stay unique across instantiations without spelling it out functions stay unique across instantiations without spelling it out
yourself; an explicit `native_type_pack_name` on the field still overrides yourself; an explicit `native_type_pack_name` on the field still overrides
this. More than one template parameter is supported by separating them with this. More than one template parameter is supported by separating them with
commas and using indexed placeholders: commas:
```cpp ```cpp
struct Pair (native_type_template: "Native::Pair<{{T0}}, {{T1}}>") { struct Pair (native_type_template: "Native::Pair") {
value: int32; value: int32;
} }
@@ -283,6 +288,7 @@ commas and using indexed placeholders:
} }
``` ```
which produces `Native::Pair<Native::Key, Native::Value>`.
`native_type_template_arg` is only valid on fields whose type is a struct `native_type_template_arg` is only valid on fields whose type is a struct
(or vector of structs) that declares `native_type_template`. (or vector of structs) that declares `native_type_template`.
+8 -27
View File
@@ -1337,35 +1337,16 @@ CheckedError Parser::ParseField(StructDef& struct_def) {
} }
} }
// Substitute each argument for its placeholder: `{{T0}}`, `{{T1}}`, etc. // The native type is `native_type_template`, the bare template name,
// For a single argument, the unindexed `{{T}}` spelling is also // with the arguments appended as a trailing, angle-bracketed template
// accepted (equivalent to `{{T0}}`). // argument list, e.g. "Native::Box" + ["Native::TypeA"] ->
std::string native_type_str = native_type_template->constant; // "Native::Box<Native::TypeA>".
std::string native_type_str = native_type_template->constant + "<";
for (size_t i = 0; i < args.size(); ++i) { for (size_t i = 0; i < args.size(); ++i) {
std::vector<std::string> placeholders = { "{{T" + NumToString(i) + if (i) native_type_str += ", ";
"}}" }; native_type_str += args[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;
} }
} native_type_str += ">";
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<n>}}' placeholder beyond the " +
NumToString(args.size()) +
" template argument(s) given in 'native_type_template_arg'");
auto native_type_val = new Value(); auto native_type_val = new Value();
native_type_val->type = native_type_template_arg->type; native_type_val->type = native_type_template_arg->type;
+2 -2
View File
@@ -24,12 +24,12 @@ table Matrix (native_type:"Native::Matrix") {
// field via `native_type_template_arg` instead of needing one flatbuffers // field via `native_type_template_arg` instead of needing one flatbuffers
// struct declaration per specialization (compare to Vector3D/Vector3DAlt // struct declaration per specialization (compare to Vector3D/Vector3DAlt
// above). // above).
struct Tagged (native_type_template: "Native::Tagged<{{T}}>") { struct Tagged (native_type_template: "Native::Tagged") {
value:int32; value:int32;
} }
// A native type template that takes more than one argument. // A native type template that takes more than one argument.
struct Pair (native_type_template: "Native::Pair<{{T0}}, {{T1}}>") { struct Pair (native_type_template: "Native::Pair") {
value:int32; value:int32;
} }
-8
View File
@@ -106,14 +106,6 @@ void ErrorTest() {
"struct-typed fields"); "struct-typed fields");
TestError( TestError(
"struct X (native_type_template: \"Foo\") { Y:int; } " "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:\"\"); }", "table T { y:X (native_type_template_arg:\"\"); }",
"empty template argument"); "empty template argument");
TestError("{}", "no root"); TestError("{}", "no root");