前回のStateパターンのお話、おかげさまでかなり好評だったようです。流行のトレンドを追いかけるのはドキドキがいっぱいでモチベーションを維持する良い材料ではありますが、トレンドを楽しむにも「やりたい事をカタチにする」ために設計と実装の腕を磨いておかにゃなりませんですね。Stateパターンを適用すれば、状態(State)と事象(Event)および状態遷移表に基づいて見通しが良く変更に強い(かつ柔軟な)コードに落とすことができます。
状態遷移表に基づいてコードに落とすのは単調な作業です。状態の数だけclassを起こし、それぞれのclassに事象の数だけメソッドを定義するので、どのclassもまったく同じ構造です。お定まりのパターンを何度も淡々を繰り返すのは退屈ですが、計算機はこのテの作業が大好きです。文句も言わず嬉々としてやってくれます。今回のお題は「状態遷移表からC#コードを自動生成させてみたよ」です。
Hello, world
ソースコードって、つまりはテキストですよね。ですからテキストで与えられたひな型と変換規則(ここでは状態遷移表)、与えられた変換規則をひな型に適用してテキストを出力する適当なツールがあればいい。嬉しいことに、Visual Studio 2005以降ではTextTransformという名のテキスト→テキスト変換ツール「T4(Text To Text Transform)」が組み入れられています。実行形式TextTransform.exeは、Visual Studio 2010をデフォルト・インストールしていれば「C:\Program Files\Common Files\Microsoft Shared\TextTemplating\10.0」にあり(64bit Windowsなら「C:\Program Files (x86)\...」)、ここにPathを通せばコマンドラインからも利用できます。
T4 templateの使い方
まず、C#コンソールアプリケーションプロジェクト「T4demo」を用意します。つぎに[新しい項目の追加]で「テキストテンプレート」を選択します。ファイル名はHello.ttとしておきましょうか。
デフォルトでは<#@ output extension=".txt" #>によって生成されるファイルの拡張子が.txtになっているので.csに変更し、やおらC#コードを書きます。
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> using System; class Hello { static public void say(string name) { Console.WriteLine("Hello, {0}",name); } }
ここでいったんsaveすると、Hello.ttから生成されたコード「Hello.cs」がプロジェクトに追加されます。
Program.csにHello.say()を呼び出すよう、少しだけ修正します。
namespace T4demo { class Program { static void Main() { Hello.say("T4"); } } }
これで完了。buildするとHello.ttから生成されたHello.csとProgram.csがコンパイルされT4demo.exeが作られます。実行してみてください。"Hello, T4"が出力されましたか?
<# コードの埋め込み #>
次に、"Hello, T4"を3回出力します。Hello.ttでConsole.WriteLine(...)を3つ書き連ねてもいいけども、それじゃあまりにつまらない。for-loopで繰り返しましょう。
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> using System; class Hello { static public void say(string name) { <# for ( int i = 0; i < 3; ++i ) { #> Console.WriteLine("Hello, {0}",name); <# } #> } }
このテンプレートからは、以下のコードが生成されます。
using System; class Hello { static public void say(string name) { Console.WriteLine("Hello, {0}",name); Console.WriteLine("Hello, {0}",name); Console.WriteLine("Hello, {0}",name); } }
要するに<# と #>で囲まれた部分がC#の文として埋め込まれるわけです。
<#= 式の展開 #>
<#= と #>で囲まれた式は、その評価値が出力テキストに展開されます。
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> using System; class Hello { static public void say(string name) { Console.WriteLine("Hello, {0}",name); <# foreach ( string fruit in new string[] { "apple", "banana", "cherry" }) { #> Console.WriteLine("I like <#= fruit.ToUpper() #>"); <# } #> } }
このテンプレートからは、以下のコードが生成されます。
using System; class Hello { static public void say(string name) { Console.WriteLine("Hello, {0}",name); Console.WriteLine("I like APPLE"); Console.WriteLine("I like BANANA"); Console.WriteLine("I like CHERRY"); } }