F#でT4の実行時テキスト生成を使用する

概要

T4 (Text Template Transfomration Toolkit) を利用して実行時テキスト生成をF#にて行う。 T4がサポートする言語はC#, VisualBasicのみだが、C#プロジェクトでテンプレートを作成し、別のF#プロジェクトからそれを参照するという形でF#でもT4を利用することが可能である。 以下にその手順を示す。

環境

目次

手順

前準備

  • F# .Net Core コンソールアプリのプロジェクトを作成しておく
  • Visual StudioのT4拡張をインストールしておく
    • 標準だとVisual Studioでttファイルを開いたときにコードハイライトが効かなくて見づらい

marketplace.visualstudio.com

f:id:hkou:20200509084111p:plain

Visual Studioをダークテーマにしている場合、標準のバックグラウンドカラーではとても見づらくなるので変更する。

  1. メニューからツール→オプション
  2. tangible T4 Editor → Editor
  3. T4 Background Colorを選択し、カラーピッカーからお好みの色を選択する

f:id:hkou:20200509085156p:plain

テンプレート用C#プロジェクトを追加

  • クラスライブラリ (.Net Core) (C#)で同じソリューション内にプロジェクトを追加する。
  • F#のプロジェクトから上記のプロジェクトへの参照を追加しておく

テンプレートを追加

ランタイムテキストテンプレート を選択して新規追加する。

f:id:hkou:20200509082120p:plain

テンプレートプロジェクトの .csproj を直接開いて先程追加した .ttファイルの Generator の項目を TextTemplatingFileGenerator から TextTemplatingFilePreprocessor に変更する。

  <ItemGroup>
    <None Update="TestTemplate.tt">
      <Generator>TextTemplatingFileGenerator</Generator>
      <LastGenOutput>TestTemplate.cs</LastGenOutput>
    </None>
  </ItemGroup>

↓↓↓ 変更後 ↓↓↓

  <ItemGroup>
    <None Update="TestTemplate.tt">
      <Generator>TextTemplatingFilePreprocessor</Generator>
      <LastGenOutput>TestTemplate.cs</LastGenOutput>
    </None>
  </ItemGroup>

テンプレートに渡すモデルを定義

public class TemplateModel
{
    public string Name { get;  }
    public int Age { get;  }
    public TemplateModel(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

モデルを渡せるようにpartial classを定義

追加したテンプレートファイルと同名のクラスをpartial classとして定義し、先程追加したモデルをコンストラクタから受け取れるようにする。

public partial class TestTemplate
{
    public TemplateModel Model { get; }
    public TestTemplate(TemplateModel model)
    {
        Model = model;
    }
}

テンプレートファイルを修正

ジェネレータ部分のコードにフルパスでライン表示が出てしまうので linePragmas="false" を指定する。

<#@ template language="C#" linePragmas="false" #>

上で定義したモデルを利用したテンプレートを記述する。

<#@ template language="C#" linePragmas="false" #>

<#= Model.Name #> のねんれいは <#= Model.Age #> 歳です。

テンプレートプロジェクトに System.CodeDomを追加

このままテンプレートプロジェクトをビルドするとコンパイルエラーが発生するため System.CodeDom を参照に追加する。

f:id:hkou:20200509090503p:plain

F#側で利用するコードを書く

  • モデルに値を設定する
  • templateクラスのコンストラクタにモデルを渡す
  • .TransformText() メソッドで適用後のテンプレート文字列を取得
open System
open Templates

[<EntryPoint>]
let main argv =
    let model = TemplateModel("シャミ子", 16)
    let template = TestTemplate(model)

    printfn "%s" <| template.TransformText()
    0

実行結果

f:id:hkou:20200509091546p:plain

参考

docs.microsoft.com

neue.cc