CSharpGL(12)用T4模板生成CSSL及其renderer代码

CSharpGL(12)用T4模板生成CSSL及其renderer代码

CSSL+Renderer

严格来说本篇不直接涉及OpenGL,但磨刀不误砍柴工,本篇仍算CSharpGL里。

上一篇制作了CSSL。CSSL和Renderer配合,就定义了一个渲染OpenGL元素的完整流程。本篇用T4模板来帮助开发者自动生成这个简单的代码框架。

下载

本篇是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)

 

如何使用?

设计一个系统前,要想好用户会期望如何使用这个系统。当我想创建一个新的OpenGL元素时,只需在一处提供此元素的名字,就足以确定它的CSSL+Renderer框架。

所以,我需要写一个T4模板,提供此名字,然后自动生成2个文件,分别存放CSSL和Renderer代码。

T4如何生成多个文件?

为T4重定向输出位置即可。步骤如下。

使用ttinclude文件

创建一个以ttinclude为扩展名的文件,输入以下内容。后面将在tt文件中引用此文件。

  1 <#@ assembly name="System.Core"#>
  2 <#@ assembly name="EnvDTE"#>
  3 <#@ import namespace="System.Collections.Generic"#>
  4 <#@ import namespace="System.IO"#>
  5 <#@ import namespace="System.Text"#>
  6 <#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
  7 
  8 <#+
  9 
 10 // T4 Template Block manager for handling multiple file outputs more easily.
 11 // Copyright (c) Microsoft Corporation.  All rights reserved.
 12 // This source code is made available under the terms of the Microsoft Public License (MS-PL)
 13 
 14 // Manager class records the various blocks so it can split them up
 15 class Manager
 16 {
 17     public struct Block {
 18         public String Name;
 19         public int Start, Length;
 20     }
 21 
 22     public List<Block> blocks = new List<Block>();
 23     public Block currentBlock;
 24     public Block footerBlock = new Block();
 25     public Block headerBlock = new Block();
 26     public ITextTemplatingEngineHost host;
 27     public ManagementStrategy strategy;
 28     public StringBuilder template;
 29     public String OutputPath { get; set; }
 30 
 31     public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader) {
 32         this.host = host;
 33         this.template = template;
 34         OutputPath = String.Empty;
 35         strategy = ManagementStrategy.Create(host);
 36     }
 37 
 38     public void StartBlock(String name) {
 39         currentBlock = new Block { Name = name, Start = template.Length };
 40     }
 41 
 42     public void StartFooter() {
 43         footerBlock.Start = template.Length;
 44     }
 45 
 46     public void EndFooter() {
 47         footerBlock.Length = template.Length - footerBlock.Start;
 48     }
 49 
 50     public void StartHeader() {
 51         headerBlock.Start = template.Length;
 52     }
 53 
 54     public void EndHeader() {
 55         headerBlock.Length = template.Length - headerBlock.Start;
 56     }    
 57 
 58     public void EndBlock() {
 59         currentBlock.Length = template.Length - currentBlock.Start;
 60         blocks.Add(currentBlock);
 61     }
 62 
 63     public void Process(bool split) {
 64         String header = template.ToString(headerBlock.Start, headerBlock.Length);
 65         String footer = template.ToString(footerBlock.Start, footerBlock.Length);
 66         blocks.Reverse();
 67         foreach(Block block in blocks) {
 68             String fileName = Path.Combine(OutputPath, block.Name);
 69             if (split) {
 70                 String content = header + template.ToString(block.Start, block.Length) + footer;
 71                 strategy.CreateFile(fileName, content);
 72                 template.Remove(block.Start, block.Length);
 73             } else {
 74                 strategy.DeleteFile(fileName);
 75             }
 76         }
 77     }
 78 }
 79 
 80 class ManagementStrategy
 81 {
 82     internal static ManagementStrategy Create(ITextTemplatingEngineHost host) {
 83         return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
 84     }
 85 
 86     internal ManagementStrategy(ITextTemplatingEngineHost host) { }
 87 
 88     internal virtual void CreateFile(String fileName, String content) {
 89         File.WriteAllText(fileName, content);
 90     }
 91 
 92     internal virtual void DeleteFile(String fileName) {
 93         if (File.Exists(fileName))
 94             File.Delete(fileName);
 95     }
 96 }
 97 
 98 class VSManagementStrategy : ManagementStrategy
 99 {
100     private EnvDTE.ProjectItem templateProjectItem;
101 
102     internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host) {
103         IServiceProvider hostServiceProvider = (IServiceProvider)host;
104         if (hostServiceProvider == null)
105             throw new ArgumentNullException("Could not obtain hostServiceProvider");
106 
107         EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
108         if (dte == null)
109             throw new ArgumentNullException("Could not obtain DTE from host");
110 
111         templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
112     }
113 
114     internal override void CreateFile(String fileName, String content) {
115         base.CreateFile(fileName, content);
116         ((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
117     }
118 
119     internal override void DeleteFile(String fileName) {
120         ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
121     }
122 
123     private void FindAndDeleteFile(String fileName) {
124         foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems) {
125             if (projectItem.get_FileNames(0) == fileName) {
126                 projectItem.Delete();
127                 return;
128             }
129         }
130     }
131 }#>
Dump2MultipleFiles.ttinclude

 

 

指定要输出的文件名

像下面这样,即可指定多个输出文件了。

 

 1 <#@ template debug="false" hostspecific="true" language="C#" #>
 2 <#@ output extension=".cs" #>
 3 <#@ include file=".\Dump2MultipleFiles.ttinclude" #>
 4 <# string ShaderName = "SomeShader"; #>
 5 <# string ClassName = ShaderName + "Renderer"; #>
 6 <# string VertexShaderName = ShaderName + "Vert"; #>
 7 <# string FragmentShaderName = ShaderName + "Frag"; #>
 8 
 9 <# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
10 <#
11     // 下述内容写入ShaderName + ".cs"文件
12 manager.StartBlock(ShaderName + ".cs");
13 #>
14 namespace CSharpShaders
15 {
16     class <#= VertexShaderName #> : VertexCSShaderCode
17     { 
18     }
19 }
20 <#
21     // 上述内容写入ShaderName + ".cs"文件
22     manager.EndBlock();
23 #>
24 <#
25     // 下述内容写入ClassName + ".cs"文件
26     manager.StartBlock(ClassName + ".cs");
27 #>
28 namespace ShaderLab
29 {
30     public class <#= ClassName #> : RendererBase
31     {
32     }
33 }
34 
35 <#
36     // 上述内容写入ClassName + ".cs"文件
37     manager.EndBlock();
38 
39     // 最后要写上这句话
40     manager.Process(true);
41 #>

总结

本篇使用T4模板生成多个代码文件,其方法是通用的。

你可能感兴趣的:(CSharpGL(12)用T4模板生成CSSL及其renderer代码)