渲染完成后能够改变的模板引擎
Template Engine Capable of Altering after Render has been done
你好
我需要创建一个模板,即"dynamic",我将解释"Dynamic"的含义:
我需要一个模板,将其呈现为文本文件(准确地说是 C++ 代码)。
用户将能够更改生成的文件中的某些内容。
一段时间后,一个进程 运行 更新生成的文件,我将能够 "spot" 模板区域在哪里并相应地更新它们。
我的努力
目前,我使用 "T4 Template" 创建初始渲染,
在模板中,我在以后需要识别的区域植入了 C++ 风格的注释。
和另一个找到该区域的代码,并重新生成那些 "Comment blocks" 之间的内容。
问题是生成样板的代码和更新区域的代码不同,这让我很头疼,而且功能有问题。
写起来不是很直观,用户(使用生成代码的人)需要知道不要碰"Comment Blocks"。
问题
- 我如何在生成的文件中识别 location/blocks 而没有 "littering" 带有 "comment"/"unimportant" 文本的文件?
- 我如何统一为 "Generation" 和 "Update"
生成 "templated blocks" 的代码
- 稍后,我怎样才能让它也适用于非代码文件,
编辑
我想我不清楚我在做什么,
我正在用 C# 编写一个生成 C++ 代码的工具。
此外,T4 正是我使用的,但是任何 tool/library 都可以使用,而 C# 库是首选
任何想法将不胜感激,
谢谢。
我认为你的做法是错误的。你这里有一个XY problem。允许您的用户仅修改生成文件的一部分,然后尝试检测该部分,如您所见,这非常令人头疼。
相反,更好的解决方案是让生成的文件完全不可修改,并提供一些配置。例如,您可以有一个配置文件,用户可以在其中添加自己的数据成员、它们的初始化程序等。
这样您就可以清楚地分离系统的各个部分。
用户所做的修改现在可以轻松地进行下一次迭代,您可以轻松地重新生成输出。
+------------------+
| Input: Template | ------
+------------------+ \
|
+------------------+ | Generator code +-------------------------+
| Input: Config | -------+----------------------> | Output: Generated code |
+------------------+ | |-------------------------+
|
+------------------+ |
| Input: Config | --------/
+------------------+
这个系统也可以用来生成非代码。
现在,我相信你的问题一方面完全是 ["open" 和 "opinion based"],另一方面是 ["why is this code not working" 没有显示代码].. 但我想尝试用你现在的 "improvement" 的想法指出一些问题。
Q2: How can I unify the code that generate the "templated blocks" both for "Generation" and "Update"
我坚信你不应该,至少现在不应该。原因如下:
- 'generate'和'update'发生在不同的方向;第一个是 t4template->content,第二个是 content->t4template
- 这两个方向形成不同的功能
- 这些方向中至少有一个需要另一个方向中不存在的复杂逻辑
- 'generate'基于T4引擎,而'update'可能根本无法使用
- ..可能还有许多其他原因,但这就足够了
Q3: Later on, How can i make it work on Non-Code files too
T4引擎不知道你现在生成的是C++文件。 T4 只作用于"text files"层。如果您现在的流程有效,您现在应该可以 "generate" 任何文本文件。 "update" 部分有点棘手,因为它取决于您如何实现它。如果您 assumed/used 与 C++ 语法有任何关联,那么您就有问题了。 (猜猜为什么 T4 模板被称为 'text templating engine',与实际生成的代码语言无关)如果你保持它干净并且像在自由格式的文本文件上一样工作,那么你已经可以安全地工作了,嗯,自由格式文本文件。
Q1: How can I recognize location/blocks in a generated file without "littering" the file with "comment"/"unimportant" text?
嗯,基本上,你不能 and/or 不应该。考虑一个聪明的想法,即保留一个隐藏的数据库,该数据库会记住每个文件的文本位置。对于您要放入文件中的每条评论,您都会在数据库中放入一行,即 file: BAR\FOO.CPP | FROM: line 120 char 1 | TO: line 131 char 15 | XXX: yyy | ZZZ: aaa
。这与在文件中添加注释几乎没有区别,所有信息都被保留,文件现在是干净的,对吧?
没有。那是因为您想检测发生了什么变化。让我们举一个高度人为的例子,这是生成的文件,其中包含由数据库管理的 不可见标记 。每个 @
字符表示一个标记,无论是 start/stop/metainfo 没关系:
class FooBar : public @BaseClass@
{
public:
@void Blargh(Whizz& output);@
@int GetAge() const;@
private:
int @shoeSize@;
@
};
那些@
当然是不可见的,它只是保存在别处的信息,用户看到的是一个干净的文件。现在,他将其编辑为:
class FooBar : public BaseClass
{
public:
template<T>
void Yeeek(T& output);
int GetAge() const;
private:
int shoeSize;
};
请注意如何添加 "template" 并将方法重命名为 "Yeeek"。那里有一些标记,我没有故意显示它们,只是看 "template<>" 行。如果不小心将一行或一个字节放置得太远或太早,导致跳过或包含一个标记过多怎么办?现在检测器和更新器可能会不小心跳过 "template<>",只需重命名方法就可以了。这不是检测器或更新程序的问题。这是 标记 不可见的问题,因此用户 无法看到 他应该将他的编辑放在哪里。
这可能是最重要的一点。但是,让我们看看更多的东西 algorithmic/technical。让我们尝试更简单的编辑。用户将文件编辑为:
class FooBarize : publ@ic BaseCl@ass
{
int goat;
@ string cheese; @
p@ublic: @
void Blargh(Whizz& output);
i@nt GetA@ge() const;
p@rivate:
int shoeSize;
};
我将 'the external database of markers' 中的那些不可见标记覆盖回这个编辑后的文件。发生了什么?简单的。用户在一个奇怪的地方又添加了两行(他没有看到标记,对吗?),并且数据库记住了旧的地方(即 'line:char',但可能是 'byte',或者其他什么)。现在当然,数据库 可能 (并且应该!)也记住文件的旧形状,所以它可以看到,即第一个 @
在 ":[=135= 之后]" 并且该过程可以尝试将其映射到新文件..但是,您已经遇到了一个非常复杂的问题,并且此编辑 微不足道 。当然,您可以要求用户输入一些关于如何更新标记的信息。但是,嘿,他看不到它们,他怎么能这样做呢?而且由于我们想对他隐藏标记,我们可能也不想问他关于更新它们的问题..
如何将文件编辑为:
struct FooBar : One,Two,Three,Four
{
void OhNoes();
};
我不想覆盖标记,因为这完全是胡说八道。现在,如何将它映射回模板? OhNoes
是否可映射到 GetAge
(删除了 const
)或映射到 Blargh
(删除了参数)?模板库 class 应该如何更新?哪个新基地才是真正的基地?或者也许所有的人?你我都无法决定,即使结合我们的人类智慧,更不用说自动化过程了。
当然,你可以把它留作极端情况,你可以向用户发出一个错误,并通知他们他们的编辑太过分了,无法分析等等。但是将更改反向映射回模型文本的复杂性仍然存在。
我想通过这些人为的例子向您展示的是,如果您想检测更改并将更改映射回原始模板,您应该在生成的内容中保留这些标记.在代码中使用这些标记可以让您快速可靠地检测:
- 哪些部分发生了变化? (-> 标记之间的内容已更改)
- 哪些部分被编辑抵消了? (->标记现在的位置与以前不同)
- 是否删除了任何部分? (-> 删除标记和内容)
- (..)
还可以让用户看到哪些部分是特殊的以便他可以合理地放置他的编辑,这可以让你忽略和不支持比"invisible markers"案例更多的极端案例。
最后,让我们举一个你已经知道的阅读世界的例子。 T4 模板。所有那些丑陋的 <%!@!#^$^!%@
乱扔你宝贵的模板文本。他们不能被删除吗?不能将这些保存在描述转换的单独文件中吗?或者至少在文件的开头或结尾?是的,它可以。但这会使编辑变得非常痛苦 - 我们又回到了 'invisible markers' 问题:您对内容的每次编辑都可能需要您手动更新一些不可见标记的位置。
在生成的内容中保留标记。
让您的用户了解生成和检测以及特殊区域。
如果对他们来说太复杂,请将用户更改为技术性更强的组,或者培训您的用户群以提高技术性。或者阻止他们编辑文件。给他们一些部分访问权限,这样他们就可以编辑文件的一部分,作为摘录,而不是整个文件。将他们的编辑能力限制在绝对最低限度。也许它将允许您限制可见标记的数量,甚至可能减少到零,也许以拆分和缩小可编辑片段为代价。
你好
我需要创建一个模板,即"dynamic",我将解释"Dynamic"的含义:
我需要一个模板,将其呈现为文本文件(准确地说是 C++ 代码)。
用户将能够更改生成的文件中的某些内容。
一段时间后,一个进程 运行 更新生成的文件,我将能够 "spot" 模板区域在哪里并相应地更新它们。
我的努力
目前,我使用 "T4 Template" 创建初始渲染, 在模板中,我在以后需要识别的区域植入了 C++ 风格的注释。
和另一个找到该区域的代码,并重新生成那些 "Comment blocks" 之间的内容。
问题是生成样板的代码和更新区域的代码不同,这让我很头疼,而且功能有问题。
写起来不是很直观,用户(使用生成代码的人)需要知道不要碰"Comment Blocks"。
问题
- 我如何在生成的文件中识别 location/blocks 而没有 "littering" 带有 "comment"/"unimportant" 文本的文件?
- 我如何统一为 "Generation" 和 "Update" 生成 "templated blocks" 的代码
- 稍后,我怎样才能让它也适用于非代码文件,
编辑
我想我不清楚我在做什么,
我正在用 C# 编写一个生成 C++ 代码的工具。
此外,T4 正是我使用的,但是任何 tool/library 都可以使用,而 C# 库是首选
任何想法将不胜感激,
谢谢。
我认为你的做法是错误的。你这里有一个XY problem。允许您的用户仅修改生成文件的一部分,然后尝试检测该部分,如您所见,这非常令人头疼。
相反,更好的解决方案是让生成的文件完全不可修改,并提供一些配置。例如,您可以有一个配置文件,用户可以在其中添加自己的数据成员、它们的初始化程序等。
这样您就可以清楚地分离系统的各个部分。 用户所做的修改现在可以轻松地进行下一次迭代,您可以轻松地重新生成输出。
+------------------+
| Input: Template | ------
+------------------+ \
|
+------------------+ | Generator code +-------------------------+
| Input: Config | -------+----------------------> | Output: Generated code |
+------------------+ | |-------------------------+
|
+------------------+ |
| Input: Config | --------/
+------------------+
这个系统也可以用来生成非代码。
现在,我相信你的问题一方面完全是 ["open" 和 "opinion based"],另一方面是 ["why is this code not working" 没有显示代码].. 但我想尝试用你现在的 "improvement" 的想法指出一些问题。
Q2: How can I unify the code that generate the "templated blocks" both for "Generation" and "Update"
我坚信你不应该,至少现在不应该。原因如下:
- 'generate'和'update'发生在不同的方向;第一个是 t4template->content,第二个是 content->t4template
- 这两个方向形成不同的功能
- 这些方向中至少有一个需要另一个方向中不存在的复杂逻辑
- 'generate'基于T4引擎,而'update'可能根本无法使用
- ..可能还有许多其他原因,但这就足够了
Q3: Later on, How can i make it work on Non-Code files too
T4引擎不知道你现在生成的是C++文件。 T4 只作用于"text files"层。如果您现在的流程有效,您现在应该可以 "generate" 任何文本文件。 "update" 部分有点棘手,因为它取决于您如何实现它。如果您 assumed/used 与 C++ 语法有任何关联,那么您就有问题了。 (猜猜为什么 T4 模板被称为 'text templating engine',与实际生成的代码语言无关)如果你保持它干净并且像在自由格式的文本文件上一样工作,那么你已经可以安全地工作了,嗯,自由格式文本文件。
Q1: How can I recognize location/blocks in a generated file without "littering" the file with "comment"/"unimportant" text?
嗯,基本上,你不能 and/or 不应该。考虑一个聪明的想法,即保留一个隐藏的数据库,该数据库会记住每个文件的文本位置。对于您要放入文件中的每条评论,您都会在数据库中放入一行,即 file: BAR\FOO.CPP | FROM: line 120 char 1 | TO: line 131 char 15 | XXX: yyy | ZZZ: aaa
。这与在文件中添加注释几乎没有区别,所有信息都被保留,文件现在是干净的,对吧?
没有。那是因为您想检测发生了什么变化。让我们举一个高度人为的例子,这是生成的文件,其中包含由数据库管理的 不可见标记 。每个 @
字符表示一个标记,无论是 start/stop/metainfo 没关系:
class FooBar : public @BaseClass@
{
public:
@void Blargh(Whizz& output);@
@int GetAge() const;@
private:
int @shoeSize@;
@
};
那些@
当然是不可见的,它只是保存在别处的信息,用户看到的是一个干净的文件。现在,他将其编辑为:
class FooBar : public BaseClass
{
public:
template<T>
void Yeeek(T& output);
int GetAge() const;
private:
int shoeSize;
};
请注意如何添加 "template" 并将方法重命名为 "Yeeek"。那里有一些标记,我没有故意显示它们,只是看 "template<>" 行。如果不小心将一行或一个字节放置得太远或太早,导致跳过或包含一个标记过多怎么办?现在检测器和更新器可能会不小心跳过 "template<>",只需重命名方法就可以了。这不是检测器或更新程序的问题。这是 标记 不可见的问题,因此用户 无法看到 他应该将他的编辑放在哪里。
这可能是最重要的一点。但是,让我们看看更多的东西 algorithmic/technical。让我们尝试更简单的编辑。用户将文件编辑为:
class FooBarize : publ@ic BaseCl@ass
{
int goat;
@ string cheese; @
p@ublic: @
void Blargh(Whizz& output);
i@nt GetA@ge() const;
p@rivate:
int shoeSize;
};
我将 'the external database of markers' 中的那些不可见标记覆盖回这个编辑后的文件。发生了什么?简单的。用户在一个奇怪的地方又添加了两行(他没有看到标记,对吗?),并且数据库记住了旧的地方(即 'line:char',但可能是 'byte',或者其他什么)。现在当然,数据库 可能 (并且应该!)也记住文件的旧形状,所以它可以看到,即第一个 @
在 ":[=135= 之后]" 并且该过程可以尝试将其映射到新文件..但是,您已经遇到了一个非常复杂的问题,并且此编辑 微不足道 。当然,您可以要求用户输入一些关于如何更新标记的信息。但是,嘿,他看不到它们,他怎么能这样做呢?而且由于我们想对他隐藏标记,我们可能也不想问他关于更新它们的问题..
如何将文件编辑为:
struct FooBar : One,Two,Three,Four
{
void OhNoes();
};
我不想覆盖标记,因为这完全是胡说八道。现在,如何将它映射回模板? OhNoes
是否可映射到 GetAge
(删除了 const
)或映射到 Blargh
(删除了参数)?模板库 class 应该如何更新?哪个新基地才是真正的基地?或者也许所有的人?你我都无法决定,即使结合我们的人类智慧,更不用说自动化过程了。
当然,你可以把它留作极端情况,你可以向用户发出一个错误,并通知他们他们的编辑太过分了,无法分析等等。但是将更改反向映射回模型文本的复杂性仍然存在。
我想通过这些人为的例子向您展示的是,如果您想检测更改并将更改映射回原始模板,您应该在生成的内容中保留这些标记.在代码中使用这些标记可以让您快速可靠地检测:
- 哪些部分发生了变化? (-> 标记之间的内容已更改)
- 哪些部分被编辑抵消了? (->标记现在的位置与以前不同)
- 是否删除了任何部分? (-> 删除标记和内容)
- (..)
还可以让用户看到哪些部分是特殊的以便他可以合理地放置他的编辑,这可以让你忽略和不支持比"invisible markers"案例更多的极端案例。
最后,让我们举一个你已经知道的阅读世界的例子。 T4 模板。所有那些丑陋的 <%!@!#^$^!%@
乱扔你宝贵的模板文本。他们不能被删除吗?不能将这些保存在描述转换的单独文件中吗?或者至少在文件的开头或结尾?是的,它可以。但这会使编辑变得非常痛苦 - 我们又回到了 'invisible markers' 问题:您对内容的每次编辑都可能需要您手动更新一些不可见标记的位置。
在生成的内容中保留标记。
让您的用户了解生成和检测以及特殊区域。
如果对他们来说太复杂,请将用户更改为技术性更强的组,或者培训您的用户群以提高技术性。或者阻止他们编辑文件。给他们一些部分访问权限,这样他们就可以编辑文件的一部分,作为摘录,而不是整个文件。将他们的编辑能力限制在绝对最低限度。也许它将允许您限制可见标记的数量,甚至可能减少到零,也许以拆分和缩小可编辑片段为代价。