refl meta update

This commit is contained in:
ouczbs 2024-07-27 14:22:51 +08:00
parent 74698bdea2
commit 28ebd58bc0
10 changed files with 199 additions and 255 deletions

View File

@ -11,27 +11,18 @@ namespace refl
public static Dictionary<string, StringBuilder> FileList = new Dictionary<string, StringBuilder>(); public static Dictionary<string, StringBuilder> FileList = new Dictionary<string, StringBuilder>();
public static StringBuilder Meta = new StringBuilder(); public static StringBuilder Meta = new StringBuilder();
public static StringBuilder Output = new StringBuilder(); public static StringBuilder Output = new StringBuilder();
private ClassMetaData data = new ClassMetaData(); private ClassMetaImplData data = new ClassMetaImplData();
private GenMeta meta = new GenMeta(); private GenMeta meta = new GenMeta();
private GenMetaImpl metaImpl = new GenMetaImpl(); private GenMetaImpl metaImpl = new GenMetaImpl();
private GenMultyMeta multyMeta = new GenMultyMeta();
private Stack<string> nameSpaces = new Stack<string>(); private Stack<string> nameSpaces = new Stack<string>();
public Gen() { } public Gen() { }
public static string PreprocessTemplate(string template, string indent, string newline)
{
// 替换占位符
return template.Replace("{{ Indent }}", indent);
}
public static bool InitTemplate(string dir) public static bool InitTemplate(string dir)
{ {
string meta_file = Path.Combine(dir, "meta.liquid"); string meta_file = Path.Combine(dir, "meta.liquid");
string meta_impl_file = Path.Combine(dir, "meta_impl.liquid"); string meta_impl_file = Path.Combine(dir, "meta_impl.liquid");
string multy_meta_file = Path.Combine(dir, "multy_meta.liquid");
var parser = new FluidParser(); var parser = new FluidParser();
return GenMeta.InitTemplate(meta_file, parser) return GenMeta.InitTemplate(meta_file, parser)
&& GenMetaImpl.InitTemplate(meta_impl_file, parser) && GenMetaImpl.InitTemplate(meta_impl_file, parser);
&& GenMultyMeta.InitTemplate(multy_meta_file, parser);
} }
public void CheckDir(string? dir) public void CheckDir(string? dir)
{ {
@ -43,12 +34,10 @@ namespace refl
public void InitFile(string file_name) public void InitFile(string file_name)
{ {
Meta.AppendLine("#pragma once"); Meta.AppendLine("#pragma once");
Meta.AppendLine("namespace refl_impl{");
foreach (var name in ModuleMeta.NameSet) foreach (var name in ModuleMeta.NameSet)
{ {
var build = new StringBuilder(); var build = new StringBuilder();
build.AppendLine("#pragma once"); build.AppendLine("#pragma once");
build.AppendLine("namespace refl_impl{");
FileList.Add(name, build); FileList.Add(name, build);
} }
} }
@ -63,8 +52,6 @@ namespace refl
} }
public void OutFile(string file_name, string? dir,string target) public void OutFile(string file_name, string? dir,string target)
{ {
Meta.AppendLine("");
Output.AppendLine("}");
foreach (var pair in FileList) foreach (var pair in FileList)
{ {
string path = $"{pair.Key}_{file_name}".ToLower(); string path = $"{pair.Key}_{file_name}".ToLower();
@ -72,7 +59,6 @@ namespace refl
{ {
path = Path.Combine(dir, path); path = Path.Combine(dir, path);
} }
pair.Value.AppendLine("}");
File.WriteAllText(path, pair.Value.ToString()); File.WriteAllText(path, pair.Value.ToString());
if (pair.Key == "Meta") if (pair.Key == "Meta")
{ {
@ -119,19 +105,13 @@ namespace refl
} }
return ""; return "";
} }
public void GenClassMeta(ClassMeta cls, ClassMetaData data) public void GenClassMeta(ClassMeta cls, ClassMetaImplData data)
{ {
string fullName = GenPrefix() + cls.Name; string fullName = GenPrefix() + cls.Name;
data.FullName = fullName; data.FullName = fullName;
bool isMulty = meta.GenClassMeta(cls, data); bool isMulty = metaImpl.GenClassMeta(cls, data);
Meta.Append(metaImpl.GenClassMeta(cls, fullName, isMulty)); Meta.Append(meta.GenClassMeta(cls, fullName, isMulty));
Meta.AppendLine(""); Meta.AppendLine("");
if (isMulty)
{
var res = multyMeta.GenClassMeta(cls, fullName);
Output.Append(res);
Output.AppendLine("");
}
} }
} }
} }

View File

@ -1,48 +1,32 @@
using System.IO; using Fluid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Threading.Tasks;
using Fluid; using static System.Runtime.InteropServices.JavaScript.JSType;
namespace refl namespace refl
{ {
internal class FieldMetaData internal class MetaClassData
{
public string Name { get; set; }
public string Meta { get; set; }
public string Ref { get; set; }
public int Type { get; set; }
public FieldMetaData(FieldData data, int type)
{
Name = data.Name;
Meta = data.Meta;
Ref = data.Ref;
Type = type;
}
}
internal class ClassMetaData
{ {
public string Name { get; set; } = ""; public string Name { get; set; } = "";
public string ParentName { get; set; } = ""; public string ParentName { get; set; } = "";
public string FullName { get; set; } = ""; public string FullName { get; set; } = "";
public string MetaName { get; set; } = ""; public bool IsMultyMeta { get; set; } = false;
public int MetaType { get; set; } = 0; public bool HasMeta { get; set; } = false;
public int MemberCount { get; set; } = 0; public bool HasParent { get; set; } = false;
public int CtorCount { get; set; } = 0; public List<string> MetaList = new List<string>();
public List<FieldMetaData> FieldList = new List<FieldMetaData>();
} }
internal class GenMeta internal class GenMeta
{ {
private static IFluidTemplate? template; private static IFluidTemplate? template;
private static TemplateOptions options = new TemplateOptions(); private static TemplateOptions options = new TemplateOptions();
private MetaClassData meta = new MetaClassData();
public static bool InitTemplate(string path, FluidParser parser) public static bool InitTemplate(string path, FluidParser parser)
{ {
var meta_string = File.ReadAllText(path); var meta_string = File.ReadAllText(path);
meta_string = Gen.PreprocessTemplate(meta_string, "\t", "\r\n");
parser.TryParse(meta_string, out template, out var error); parser.TryParse(meta_string, out template, out var error);
options.MemberAccessStrategy.Register<FieldMetaData>();
if (!string.IsNullOrEmpty(error)) if (!string.IsNullOrEmpty(error))
{ {
Console.WriteLine(error); Console.WriteLine(error);
@ -50,45 +34,24 @@ namespace refl
} }
return true; return true;
} }
public bool GenClassMeta(ClassMeta cls, ClassMetaData data) public string GenClassMeta(ClassMeta cls, string fullName, bool isMulty)
{ {
data.Name = cls.Name; meta.ParentName = cls.ParentName;
data.ParentName = cls.ParentName; meta.Name = cls.Name;
bool isMulty = cls.Fields.Count > 1; meta.FullName = fullName;
if(cls.Fields.Count == 1) meta.IsMultyMeta = isMulty;
{ meta.HasMeta = false;
isMulty = cls.Fields.Keys.First() != "Meta"; meta.HasParent = cls.ParentName != "void";
}
foreach (var pair in cls.Fields) foreach (var pair in cls.Fields)
{ {
data.FieldList.Clear(); if (pair.Key.Equals("Meta"))
foreach (var field in pair.Value.MemberList)
{ {
data.FieldList.Add(new FieldMetaData(field, 1)); meta.HasMeta = true;
} }
foreach (var field in pair.Value.CtorList) meta.MetaList.Add(pair.Key);
{
data.FieldList.Add(new FieldMetaData(field, 2));
}
foreach (var field in pair.Value.MethodList)
{
int type = string.IsNullOrEmpty(field.Ref) ? 3 : 4;
data.FieldList.Add(new FieldMetaData(field, type));
}
data.MemberCount = pair.Value.MemberList.Count;
data.CtorCount = pair.Value.CtorList.Count;
data.MetaType = isMulty ? 1 : 0;
if (isMulty && pair.Key.Equals("Meta"))
{
data.MetaType = 2;
}
data.MetaName = pair.Key;
var context = new TemplateContext(data, options);
var res = template.Render(context);
Gen.FileList[pair.Key].Append(res);
Gen.FileList[pair.Key].AppendLine("");
} }
return isMulty; var context = new TemplateContext(meta, options);
return template.Render(context);
} }
} }
} }

View File

@ -1,31 +1,46 @@
using Fluid; using System.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Xml.Linq;
using static System.Runtime.InteropServices.JavaScript.JSType; using Fluid;
namespace refl namespace refl
{ {
internal class MetaImplClassData internal class FieldMetaImplData
{
public string Name { get; set; }
public string Meta { get; set; }
public string Ref { get; set; }
public int Type { get; set; }
public FieldMetaImplData(FieldData data, int type)
{
Name = data.Name;
Meta = data.Meta;
Ref = data.Ref;
Type = type;
}
}
internal class ClassMetaImplData
{ {
public string Name { get; set; } = ""; public string Name { get; set; } = "";
public string FullName { get; set; } = ""; public string FullName { get; set; } = "";
public bool IsMultyMeta { get; set; } = false; public string MetaName { get; set; } = "";
public bool HasMeta { get; set; } = false; public int MetaType { get; set; } = 0;
public int MemberCount { get; set; } = 0;
public int CtorCount { get; set; } = 0;
public List<FieldMetaImplData> FieldList = new List<FieldMetaImplData>();
} }
internal class GenMetaImpl internal class GenMetaImpl
{ {
private static IFluidTemplate? template; private static IFluidTemplate? template;
private static TemplateOptions options = new TemplateOptions(); private static TemplateOptions options = new TemplateOptions();
private MetaImplClassData impl = new MetaImplClassData();
public static bool InitTemplate(string path, FluidParser parser) public static bool InitTemplate(string path, FluidParser parser)
{ {
var meta_string = File.ReadAllText(path); var meta_string = File.ReadAllText(path);
meta_string = Gen.PreprocessTemplate(meta_string, "\t", "\r\n");
parser.TryParse(meta_string, out template, out var error); parser.TryParse(meta_string, out template, out var error);
options.MemberAccessStrategy.Register<FieldMetaImplData>();
if (!string.IsNullOrEmpty(error)) if (!string.IsNullOrEmpty(error))
{ {
Console.WriteLine(error); Console.WriteLine(error);
@ -33,21 +48,44 @@ namespace refl
} }
return true; return true;
} }
public string GenClassMeta(ClassMeta cls,string fullName, bool isMulty) public bool GenClassMeta(ClassMeta cls, ClassMetaImplData data)
{ {
impl.Name = cls.Name; data.Name = cls.Name;
impl.FullName = fullName; bool isMulty = cls.Fields.Count > 1;
impl.IsMultyMeta = isMulty; if (cls.Fields.Count == 1)
impl.HasMeta = false; {
isMulty = cls.Fields.Keys.First() != "Meta";
}
foreach (var pair in cls.Fields) foreach (var pair in cls.Fields)
{ {
if (pair.Key.Equals("Meta")) data.FieldList.Clear();
foreach (var field in pair.Value.MemberList)
{ {
impl.HasMeta = true; data.FieldList.Add(new FieldMetaImplData(field, 1));
} }
foreach (var field in pair.Value.CtorList)
{
data.FieldList.Add(new FieldMetaImplData(field, 2));
}
foreach (var field in pair.Value.MethodList)
{
int type = string.IsNullOrEmpty(field.Ref) ? 3 : 4;
data.FieldList.Add(new FieldMetaImplData(field, type));
}
data.MemberCount = pair.Value.MemberList.Count;
data.CtorCount = pair.Value.CtorList.Count;
data.MetaType = isMulty ? 1 : 0;
if (isMulty && pair.Key.Equals("Meta"))
{
data.MetaType = 2;
}
data.MetaName = pair.Key;
var context = new TemplateContext(data, options);
var res = template.Render(context);
Gen.FileList[pair.Key].Append(res);
Gen.FileList[pair.Key].AppendLine("");
} }
var context = new TemplateContext(impl, options); return isMulty;
return template.Render(context);
} }
} }
} }

View File

@ -1,46 +0,0 @@
using Fluid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace refl
{
internal class MultyMetaClassData
{
public string Name { get; set; } = "";
public string FullName { get; set; } = "";
public List<string> MetaList = new List<string>();
}
internal class GenMultyMeta
{
private static IFluidTemplate? template;
private static TemplateOptions options = new TemplateOptions();
public static bool InitTemplate(string path, FluidParser parser)
{
var meta_string = File.ReadAllText(path);
meta_string = Gen.PreprocessTemplate(meta_string, "\t", "\r\n");
parser.TryParse(meta_string, out template, out var error);
if (!string.IsNullOrEmpty(error))
{
Console.WriteLine(error);
return false;
}
return true;
}
public string GenClassMeta(ClassMeta cls, string fullName)
{
MultyMetaClassData data = new MultyMetaClassData();
data.Name = cls.Name;
data.FullName = fullName;
foreach (var pair in cls.Fields)
{
data.MetaList.Add(pair.Key);
}
var context = new TemplateContext(data, options);
return template.Render(context);
}
}
}

View File

@ -1,6 +1,6 @@
@echo off @echo off
set "source_dir=%CD%" REM 当前目录 set "source_dir=%CD%" REM 当前目录
set "destination_dir=F:\ouczbs\zengine\tools\refl" REM 指定目标目录 set "destination_dir=F:\engine\zengine\tools\refl" REM 指定目标目录
xcopy "%source_dir%\bin\Release\net8.0\win-x64\*" "%destination_dir%\" /Y xcopy "%source_dir%\bin\Release\net8.0\win-x64\*" "%destination_dir%\" /Y
xcopy "%source_dir%\template\*" "%destination_dir%\template\" /Y xcopy "%source_dir%\template\*" "%destination_dir%\template\" /Y
pause pause

View File

@ -1,9 +1,7 @@
#pragma once #pragma once
#if !defined(__cppast) #if !defined(__cppast)
#define __cppast(...) #define __cppast(...)
#else
#endif #endif
#define __Meta(...) __cppast(Meta=__VA_ARGS__) #define __Meta(...) __cppast(Meta=__VA_ARGS__)
#define UPROPERTY(...) __Meta(__VA_ARGS__) #define UPROPERTY(...) __Meta(__VA_ARGS__)
#define UFUNCTION(...) __Meta(__VA_ARGS__) #define UFUNCTION(...) __Meta(__VA_ARGS__)
@ -38,9 +36,4 @@ struct vec3{
UFUNCTION({},ref = USING_CTOR_NAME) UFUNCTION({},ref = USING_CTOR_NAME)
vec3(){} vec3(){}
} }
*/ */
namespace refl_impl {
struct Meta {};
struct vkMeta {};
struct dxMeta {};
}

View File

@ -2,10 +2,42 @@
#include "type.h" #include "type.h"
#include "guid.h" #include "guid.h"
#include "resource_bundle.h" #include "resource_bundle.h"
namespace api {
using pmr::Name;
enum class EModuleFlag : uint32_t {
Reload = 1,
};
ENABLE_BITMASK_OPERATORS(EModuleFlag);
struct ModuleInfo {
UPROPERTY()
EModuleFlag flag{ 0 };
UPROPERTY()
Name name; //!< name of the plugin
UPROPERTY()
Name prettyname; //!< formatted name of the plugin
UPROPERTY()
Name core_version; //!< version of the engine
UPROPERTY()
Name version; // !< version of the plugin
UPROPERTY()
Name linking; // !< linking of the plugin
UPROPERTY()
Name license; //!< license of the plugin
UPROPERTY()
Name url; //!< url of the plugin
UPROPERTY()
Name copyright; //!< copyright of the plugin
public:
bool IsReload() {
return !!(flag & EModuleFlag::Reload);
}
};
}
namespace engineapi namespace engineapi
{ {
struct SerializedMeta struct SerializedMeta
{ {
UPROPERTY_vk({})
UPROPERTY({}) UPROPERTY({})
Guid guid; Guid guid;
UPROPERTY({}) UPROPERTY({})
@ -13,10 +45,12 @@ namespace engineapi
UPROPERTY({}) UPROPERTY({})
string t_hash{}; string t_hash{};
UPROPERTY({}) UPROPERTY({})
string metadata; refl::Any meta;
bool operator==(const SerializedMeta& other)const {
return guid == other.guid && name == other.name && t_hash == other.t_hash;
}
}; };
struct ResourceBundle; struct ResourceBundle;
struct MetaBundle struct MetaBundle
{ {
UPROPERTY({}) UPROPERTY({})
@ -26,6 +60,12 @@ namespace engineapi
template<typename T> template<typename T>
const SerializedMeta* FetchMeta() const; const SerializedMeta* FetchMeta() const;
template<typename T>
const SerializedMeta* FetchMeta(const string_view& asset_name) const;
template<typename T>
void Add(RscHandle<T>);
bool operator==(const MetaBundle& other)const;
bool operator!=(const MetaBundle& other)const;
}; };
} }
#include "meta_bundle.inl" #include "meta_bundle.inl"

View File

@ -1,66 +1,24 @@
{{ Indent }}template<> struct _Static<{{FullName}},{{MetaName}}> { namespace refl{
{{ Indent }} using T = {{FullName}}; template<> struct Meta<{{FullName}}>{
{{ Indent }} consteval static auto __MakeStaticFields() { {%- if HasParent %}
{{ Indent }} return std::array{ using Parent = {{ ParentName }};
{{ Indent }} {%- for field in FieldList %} {%- endif %}
{{ Indent }} {%- if field.Type == 1 %} using T = {{FullName}};
{{ Indent }} refl::StaticMemberField(&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}), {%- if HasMeta %}
{{ Indent }} {%- elsif field.Type == 2 %} using Impl = gen::MetaImpl<T, string_hash("Meta")>;
{{ Indent }} refl::StaticCtorField((T::{{field.Ref}})nullptr,{{field.Meta}}), {%- endif %}
{{ Indent }} {%- elsif field.Type == 3 %} {%- if IsMultyMeta %}
{{ Indent }} refl::StaticMethodField(&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}), {%- for Meta in MetaList %}
{{ Indent }} {%- else %} static const UClass* get_{{Meta}}();
{{ Indent }} refl::StaticMethodField((T::{{field.Ref}})&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}), {%- endfor %}
{{ Indent }} {%- endif %} static const UClass* GetMeta(Name name) {
{{ Indent }} {%- endfor %} {%- for Meta in MetaList %}
{{ Indent }} }; if (name == string_hash("{{Meta}}")) {
{{ Indent }} }; return get_{{Meta}}();
{{ Indent }} }
{{ Indent }} consteval static int Size() { {%- endfor %}
{{ Indent }} return refl::fetch_meta_size<_Static<T,{{MetaName}}>>(); return nullptr;
{{ Indent }} }; }
{{ Indent }} consteval static int MemberCount() { {%- endif %}
{{ Indent }} return {{MemberCount}}; };
{{ Indent }} }; }
{{ Indent }} consteval static int CtorCount() {
{{ Indent }} return {{CtorCount}};
{{ Indent }} };
{{ Indent }}};
{{ Indent }}
{{ Indent }}template<> struct _Meta<{{FullName}},{{MetaName}}> : public refl::MetaHelp {
{{ Indent }} using T = {{FullName}};
{{ Indent }} using MyParent = {{ ParentName }};
{{ Indent }} using MyStatic = _Static<T,{{MetaName}}>;
{{ Indent }}{%-if IsMultyMeta %}
{{ Indent }} using MyMetas = class _Multy<T>;
{{ Indent }}{%- endif %}
{{ Indent }} inline static char s_data[MyStatic::Size()]{};
{{ Indent }} static auto __MakeFields() {
{{ Indent }} char* memory = &s_data[0];
{{ Indent }} return std::array{
{{ Indent }} {%- for field in FieldList %}
{{ Indent }} {%- if field.Type == 1 %}
{{ Indent }} MemberField(&T::{{field.Name}}, FName("{{field.Name}}"), memory, {{field.Meta}}),
{{ Indent }} {%- elsif field.Type == 2 %}
{{ Indent }} CtorField((T::{{field.Ref}})nullptr, memory, {{field.Meta}}),
{{ Indent }} {%- elsif field.Type == 3 %}
{{ Indent }} MethodField(&T::{{field.Name}}, FName("{{field.Name}}"), memory, {{field.Meta}}),
{{ Indent }} {%- else %}
{{ Indent }} MethodField((T::{{field.Ref}})&T::{{field.Name}}, FName("{{field.Name}}"), memory, {{field.Meta}}),
{{ Indent }} {%- endif %}
{{ Indent }} {%- endfor %}
{{ Indent }} };
{{ Indent }} };
{{ Indent }}};
{{ Indent }}{%- if MetaType == 1 %}
{{ Indent }}const refl::UClass* _Multy<{{FullName}}>::get_{{ MetaName }}()
{{ Indent }}{
{{ Indent }} static const auto s_cls = refl::UClass_Meta<T, _Meta<T,{{ MetaName }}>>();
{{ Indent }} return &s_cls;
{{ Indent }}};
{{ Indent }}{%- elsif MetaType == 2 %}
{{ Indent }}inline const refl::UClass* _Multy<{{FullName}}>::get_{{ MetaName }}()
{{ Indent }}{
{{ Indent }} return &refl::TypeInfo<T>::StaticClass;
{{ Indent }}};
{{ Indent }}{%- endif %}

View File

@ -1,9 +1,41 @@
{{ Indent }}template<> struct MetaImpl<{{FullName}}>{ namespace gen{
{{ Indent }} using T = {{FullName}}; template<> struct MetaImpl<{{FullName}}, string_hash("{{MetaName}}")> : public refl::MetaHelp {
{{ Indent }}{%- if HasMeta %} using T = {{FullName}};
{{ Indent }} using MyMeta = _Meta<T, Meta>; consteval static int MemberCount() { return {{MemberCount}}; };
{{ Indent }}{%- endif %} consteval static int CtorCount() { return {{CtorCount}}; };
{{ Indent }}{%- if IsMultyMeta %} consteval static auto Fields(){
{{ Indent }} using MyMetas = _Multy<T>; return std::make_tuple({% for field in FieldList %}FProperty(&T::{{field.Name}},"{{field.Name}}"){% unless forloop.last %}, {% endunless %}{% endfor %});
{{ Indent }}{%- endif %} }
{{ Indent }}}; static auto MakeFields() {
return std::array{
{%- for field in FieldList %}
{%- if field.Type == 1 %}
MemberField(&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}),
{%- elsif field.Type == 2 %}
CtorField((T::{{field.Ref}})nullptr, {{field.Meta}}),
{%- elsif field.Type == 3 %}
MethodField(&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}),
{%- else %}
MethodField((T::{{field.Ref}})&T::{{field.Name}}, FName("{{field.Name}}"), {{field.Meta}}),
{%- endif %}
{%- endfor %}
};
};
};
}
{%- if MetaType == 1 %}
namespace refl{
const UClass* Meta<{{FullName}}>::get_{{ MetaName }}()
{
static const UClass_Meta<T, gen::MetaImpl<T, string_hash("{{ MetaName }}")>> s_cls{};
return &s_cls;
};
}
{%- elsif MetaType == 2 %}
namespace refl{
inline const UClass* Meta<{{FullName}}>::get_{{ MetaName }}()
{
return &TypeInfo<T>::StaticClass;
};
}
{%- endif %}

View File

@ -1,14 +0,0 @@
{{ Indent }}template<> struct _Multy<{{FullName}}>{
{{ Indent }} using T = {{FullName}};
{{ Indent }}{%- for Meta in MetaList %}
{{ Indent }} static const refl::UClass* get_{{Meta}}();
{{ Indent }}{%- endfor %}
{{ Indent }} static const refl::UClass* GetMeta(const Name& name) {
{{ Indent }}{%- for Meta in MetaList %}
{{ Indent }} if (name == FName("{{Meta}}")) {
{{ Indent }} return get_{{Meta}}();
{{ Indent }} }
{{ Indent }}{%- endfor %}
{{ Indent }} return nullptr;
{{ Indent }} }
{{ Indent }}};