T4嵌り

  • else を使う時は <#} else { #> と記述しないといけない
  • <#+ #>で関数やクラスを定義する場合は独立したファイルに記述し、それを使用する時は <#@ include file=”Common.tt” #> でインクルードしないといけない。
  • <#@ include file=”Common.tt” #> を記述する度に改行が追加される。
    下記の様に空のコントロールブロックを追加すると何故か回避される。
    <#@ include file=”Common.tt” #><##>
    CRLFになっていない場合も発生する場合もあるらしい。
    参考:stackoverflow
  • コメントを入れる <# // コメント #>

基本

  • テンプレートのメタ定義
    • 使用する言語の定義
    • 出力ファイル拡張子
    • 参照アセンブリ
    • インポート宣言
    • 文中への直接コード埋め込み
      <#= “Hello” #>
    • インデントを入れる
      PushIndent, PopIndent, ClearIndent
    • 繰り返し構文
      public class Hello
      {
      <# foreach(int idx in Enumerable.Range(0, 3)) { #>
      ああああ
      <# } #>
      }

      出力

      public class Hello
      {
      ああああ
      ああああ
      ああああ
      }
    • C++ コード生成サンプル
  • Visual Studio のプロジェクト
    • C# プロジェクトを作る。C++ プロジェクトでは .tt は扱えない

何を自動生成するべきか

  • 欲しい物
    • デスクリプタ
      • DDONE 構造体定義
        • 構造体名
          • デスクリプタ名
        • メンバ変数名
          • パラメータ名を変数名
          • ビット幅をビットセットで表す
          • reserved で reserved を挿入
      • セッター・ゲッター・ダンパー
        • メンバ変数名
        • 引数の型をどう定義するか?
          • スクレープ時に _count で int にするとか?
          • struct の xml だけ手で書く
            この時 enum 名を自分で書く
          • enum 生成だけ html テーブルを指定して列挙子を xml 化する
            •  html になっている時点で xml 化する必要が無い気はする。
            • 同じ DOM 構造に落とし込めるのでその後の T4 テンプレートが作りやすくなる。
      • DONE セッター・ゲッターのアサート
        • ポインタならヌルチェックを挟むだけ
        • その他の不正な値を取得する必要がある
          • ⇒ もうこれはやらなくてもいいのでは?
    • enum
      • どうやってスクレイピングするか?
        • デスクリプタの変数名から判定できるか?
          多分難しい。
  • 自動生成して活用できる事
    • C#向けにもコード生成ができる
    • この位かな…。
  • C# のデータ型
    http://www.kumei.ne.jp/c_lang/cs/cs_05.htm

xml から enum を自動生成する

Enum.xml

<?xml version="1.0" encoding="utf-8" ?>
<EnumType Name="Mode" UnderlyingType="Int16">
  <Enumerator Name="ModeA" Value="0"/>
  <Enumerator Name="ModeB" Value="1"/>
  <Enumerator Name="ModeC" Value="2"/>
</EnumType>

Enum.tt

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".h" #>
<#  
    XmlDocument doc = new XmlDocument();
    string absolutePath = @"D:\prj\zikken\t4\c++_class_simple\TextTemplate\TextTemplateModel\Enum.xml";
    doc.Load(absolutePath);
    var rootNodes = doc.SelectNodes("/EnumType");
#>
enum class <#=rootNodes[0].Attributes["Name"].InnerText #>
{
<#  foreach (XmlNode node in doc.SelectNodes("/EnumType/Enumerator")){ #>
    <#=node.Attributes["Name"].InnerText #> = <#=node.Attributes["Value"].InnerText #>
<#    } #>
};

Enum.h

enum class Mode
{
    ModeA = 0
    ModeB = 1
    ModeC = 2
};

html のテーブルから enum を生成する

html の任意箇所の xpath を取得する。FireFox の場合

要素の検証 -> 取得したいタグを右クリック -> コピー -> xpath

/html/body/p[11]/table

/html/body/p[11]/table/tbody/tr[2]/td[4]

/html/body/p[11]/table/tbody/tr[3]/td[1]

※html のテーブルの要素のインデックスは 1 から始まる点に注意。

メモ

  • html を xml として読み込まそうとするとタグの開始・終了が合わない等のエラーが出る事がある
  • sjis の場合は XmlDocument で読み込めない。読み込み時にエラーが発生する。
  • xhtml が元からあれば直接読み込める
  • xml があればラッキー

XmlDocument によるアクセス

TODO: 全然まとまっていない。

  • 要素下記で hoge が要素名、foo が値
    <hoge>foo</hoge>
    XElement::Element(hoge).Value で “foo” が取得される。
  • XElement はどうやって取得するか?
    • XDocument xDoc = XDocument.Load(new XmlNodeReader(xmlDoc));
    • XElement は XDocument からの要素アクセスに使われる。

取り敢えずこれ使え

XDocument を使う

参考:

html

<html>
<body>
<h3>Answer</h3>
<div class="right">
<table>
<tbody><tr class="headings"><th colspan="4">Types</th></tr>
<tr><td><b>Type</b></td><td><b>Size</b></td><td><b>Range</b></td><td><b>Accuracy</b></td></tr>
<tr><td>float</td><td>32 bits</td><td>3:0</td><td>comment1</td></tr>
<tr><td>double</td><td>64 bits</td><td>7:4</td><td>comment2</td></tr>
</tbody></table>
</div>
</body></html>

ソース

    class ParseHtml
    {
        public void Parse()
        {
            var path = @"D:\prj\zikken\t4\c++_class_simple\TextTemplate\TextTemplateModel\Size_of_Floats.html";
            var query = "/html/body/div/table/tbody"; //" / html/body/h3";
#if false
            // TODO: XElement だと読めない
            var xelm = XElement.Load(path);
            var dbElement = xelm.XPathSelectElement(query);
#else
            var doc = XDocument.Load(path);
            // 要素指定アクセス
            var dbElement = doc.XPathSelectElement(query);
#endif
            Console.WriteLine(dbElement.Value + "\n");

            // テーブルダンプ
            IEnumerable<XElement> trs = dbElement.Elements("tr");
            foreach (XElement tr in trs)
            {
                IEnumerable<XElement> tds = tr.Elements("td");
                foreach (XElement td in tds)
                {
                    if (td == null) continue;
                    Console.WriteLine(td.Value + "\t");
                }
            }
        }
    }

参考:IEnumerable について