你好
我需要创建一个模板,即动态",然后我将解释"动态":
的含义我需要一个模板,它被渲染成文本文件(确切地说是c ++代码)。
用户将能够更改生成的文件中的一些内容。
过了一段时间,运行一个进程来更新生成的文件,我能够发现"发现"模板区域在哪里并相应地更新它们。
目前,我正在使用" T4模板"创建初始渲染, 在模板中,我在以后需要识别的区域上植入C ++样式注释。
和另一个代码,找到这些区域,并重新生成那些"注释块之间的内容"。
问题在于它与生成锅炉板的代码不同,并且更新了区域,这使我花费了很多头痛和错误的功能。
编写起来不是很直观,而且用户(使用生成代码的用户)需要知道不要触摸"注释块"。
我想我不清楚我在做什么,
我正在用C#编写一个生成C ++代码的工具
此外,T4正是我所使用的,但任何工具/库都可以在首选C#库时使用
任何想法都将受到高度赞赏,
感谢。
答案 0 :(得分:1)
我认为你的做法是错误的。你这里有一个XY problem。允许您的用户仅修改生成的文件的一部分,然后尝试检测该部分,这是您看到的很多问题。
相反,更好的解决方案是让生成的文件完全不可修改,并提供一些配置。例如,您可以拥有一个配置文件,用户可以在其中添加自己的数据成员,初始化程序等等。
通过这种方式,您可以清楚地分离系统的各个部分。 用户完成的修改现在可以轻松地进行到下一次迭代,您可以轻松地重新生成输出。
+------------------+
| Input: Template | ------
+------------------+ \
|
+------------------+ | Generator code +-------------------------+
| Input: Config | -------+----------------------> | Output: Generated code |
+------------------+ | |-------------------------+
|
+------------------+ |
| Input: Config | --------/
+------------------+
此系统也可用于生成非代码。
答案 1 :(得分:1)
现在,我相信你的问题完全是["开放"和#34;意见为基础"]一方面,["为什么这段代码不起作用"没有显示代码]在另一边..但我想尝试指出一些问题"改进"你现在有。
Q2:如何统一生成"模板块的代码"对于" Generation"和"更新"
我坚信你不应该,至少现在不行。这就是原因:
问题3:稍后,我如何才能使其在非代码文件上工作
T4引擎不知道你现在生成的是C ++文件。 T4仅适用于一层"文本文件"。如果您现在的流程有效,您应该能够生成"生成"任何文本文件现在已经存在。 "更新"部分有点棘手,因为它取决于你如何实现它。如果您假设/使用了与C ++语法的任何相关性,那么您就会遇到问题。 (猜猜为什么T4模板被称为'文本模板引擎',与实际生成的代码语言无关)如果你保持干净并像在自由格式的文本文件上一样工作,那么你已经安全地工作,以及自由格式的文本文件。
Q1:如何在没有"乱丢垃圾的情况下识别生成的文件中的位置/块?该文件包含"评论" /"不重要"文本?
嗯,基本上,你不能和/或不应该。考虑一个聪明的想法,即保留一个隐藏的数据库,记住每个文件的文本位置。对于您将放入文件的每条评论,您在数据库中添加一行,说file: BAR\FOO.CPP | FROM: line 120 char 1 | TO: line 131 char 15 | XXX: yyy | ZZZ: aaa
。在文件中添加注释几乎没有区别,所有信息都会被保留,文件现在还是干净的,对吗?
不。这是因为您想要检测已发生变化的内容。让我们来看一个非常人为的例子,这里生成的文件包含由数据库管理的隐形标记。每个@
字符表示一个标记,无论是start / stop / metainfo nevermind:
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;
};
请注意&#34;模板&#34;添加并将方法重命名为&#34; Yeeek&#34;。那里有一些标记,我没有故意显示它们,只需查看&#34;模板&lt;&gt;&#34;线。如果意外地将一行或一个字节放置得太远或太早,那么会跳过或包含一个太多的标记怎么办?现在,检测器和更新器可能会意外跳过&#34; template&lt;&gt;&#34;,并且完全乐意重命名该方法。这对探测器或更新器来说不是问题。这是一个标记不可见的问题,因此用户无法看到他应该在何处进行编辑。
这可能是最重要的一点。但是,让我们看看更多的算法/技术。让我们尝试更简单的编辑。用户将文件编辑为:
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;
};
我覆盖了标记的外部数据库中的那些不可见标记&#39;回到这个编辑过的文件。发生了什么事?简单。用户在一个奇怪的地方添加了两行(他没有看到标记,对吗?),数据库记得旧地方(即&#39; line:char&#39;,但可能是&#39; byte&#39; ;或者其他什么)。当然,数据库可能(并且应该!)也记住文件的旧形状,因此它可以看到,即第一个@
在&#34;:public&#34;之后。并且该过程可以尝试将其映射到新文件..但是,您已经有一个非常复杂的问题,并且此编辑琐碎。当然,您可以要求用户输入有关如何更新标记的一些信息..但是,嘿,他没有看到它们,他怎么能这样做?既然我们想隐藏他的标记,我们可能也不想问他有关更新它们的信息。
如何将文件编辑为:
struct FooBar : One,Two,Three,Four
{
void OhNoes();
};
我并不关心覆盖标记,因为它完全是胡说八道。现在,如何将其映射回模板?是OhNoes
是否可以GetAge
(const
已删除)或Blargh
(参数已移除)?如何更新模板基类?哪个新基地才是真正的基础?或者也许都是他们所有人?你和我都不能决定,即使是我们的综合人工智能,也没有提到自动化过程。
当然,您可以将其作为一个角落案例,您可以向用户发出错误并告知他们他们的编辑进行到远程并且无法分析等等。但是,将更改反向映射到模型文本的复杂性仍然存在。
我想通过这些人为的例子向您展示,如果您想要检测并将更改映射回原始模板,您应该将这些标记保留在生成的内容中。在代码中包含这些标记可以快速可靠地检测:
它还允许用户查看哪些部分是特殊的,这样他就可以合理的方式进行编辑,这样就可以忽略并且不支持更多的角落而不是&#34;隐形标记&#34;情况下。
最后,让我们来看一个你已经知道的阅读世界的例子。 T4模板。所有那些丑陋的<%!@!#^$^!%@
乱丢你珍贵的模板文字。他们不能被删除吗?这些不能保存在描述转换的单独文件中吗?或者至少在文件的开头或结尾?是的,它可以。但它会使编辑成为一种真正的痛苦 - 我们会回到“看不见的标记”中。问题:您对内容的每次编辑都可能需要您手动更新某些不可见标记的位置。
将标记保留在生成的内容中 让您的用户了解生成和检测以及特殊区域。
如果它们太复杂,请将用户更改为更技术性的群组,或者培训您的用户群更具技术性。或者阻止他们编辑文件。给他们一些部分访问权限,以便他们可以编辑部分文件,作为摘录,而不是整个文件。将编辑能力限制在绝对最小值。也许它会允许你限制可见标记的数量,甚至可能降到零,可能以分割和缩小可编辑片段为代价。