SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

状態遷移表からStateパターンを自動生成する

T4 templateで楽にコードを書こう


Penの完成

 さて、このテンプレートから生成されるState.csにはイベントを表現するclassであるknock、writeそしてContextが定義されていません。さらに、状態と事象の組に応じたアクションはテンプレートで与えておらず、これらはすべて外付けになります。アクションについてはpartial methodで差し込むことができるようにしてあります。生成されたState.csと以下に示すProgram.csとで、アプリケーション完成です。

Program.cs
using System;

// イベント: knock
public class knock {}

// 引数を持ったイベント: write
public class write {
    public string msg;
    public write(string s) { msg = s; }
}

// コンテキスト
public interface Context {
  void do_write(string s);
}

// enabed状態でwriteイベントが起こったときのアクションを追加する
partial class enabled {
  partial void _action_(write evt, Context ctx, ref State transition) {
    ctx.do_write(evt.msg);
  }
}

// ノック式ボールペン: コンテキストを実装し,Stateを内包させる。
public class Pen : Context {
  State s = State.disabled();
  public void do_write(string s) { Console.Write(s); }
  public void knock() { s = s.handle(new knock(),this); }
  public void write(string msg) { s = s.handle(new write(msg), this); }
}  

class Program {
  static void Main() {
    Pen pen = new Pen();
    pen.write("はじめは書けないけど");
    pen.knock();
    pen.write("一度ノックすれば書けます。");
    pen.knock();
    pen.write("も一度ノックすると書けなくなります。");
    pen.knock();
    pen.write("ノック式ボールペンてばそんなもの。");
    Console.WriteLine();
  }
}

 partial method定義するアクション関数「_action_」に小さなオマケを仕込みました。第3引数には遷移先状態が引き渡されますが、こいつをrefによる参照渡しにしておきました。この値を書き換えることでアクションの結果に応じて遷移先を変更できます。partial class enabledを以下のようなコードに変更することで「'。'で終わる文字列を書くと自動的にペン先の引っ込む"いらんことしぃなPen」を作ることができます。

list
// enabed状態でwriteイベントが起こったときのアクションを追加する
partial class enabled {
  partial void _action_(write evt, Context ctx, ref State transition) {
    ctx.do_write(evt.msg);
    // '。'で終わる文字列を書くと遷移先をdisabledに変更する
    if (evt.msg.EndsWith("。")) transition = State.disabled();
  }
}

 また、各StateにはそのStateに入るとき/そのStateから抜けるときに必ず行う入場動作/退場動作をpartial method「_enter_(Context ctx)/_exit_(Context ctx)」で定義できます。

C++コードを生成するには

 C#やVB.NETコードであれば.cs/.vbを1つ生成すればお終いなのですが、C++となると1つのテンプレートから.hと.cppを吐かせなくてはなりません。ここでは、コマンドラインからTextTransform.exeを起動し.h/.cppを生成します。

 TextTransform.exeのコマンドライン・オプション「-I」によって、<#@ include file="~" #>で展開されるテンプレートの置かれたディレクトリを指定できます。すなわち、生成ルールの異なる2つのテンプレートをそれぞれ異なるディレクトリに(同じファイル名で)置いておき、-Iオプションでディレクトリを指定することで異なるコードを生成させます、さらに-outオプションで生成ファイル名を指定しています。ダウンロード用サンプルファイルの、Pen_T4_cppプロジェクトにあるtransform.batをご覧ください。

transform.bat
set t4="c:\Program Files\Common Files\Microsoft Shared\TextTemplating\10.0\TextTransform.exe"
: set t4="c:\Program Files (x86)\Common Files\Microsoft Shared\TextTemplating\10.0\TextTransform.exe"
%t4% -I t4/h   -out State.h   State.tt
%t4% -I t4/cpp -out State.cpp State.tt

 いかがでしょうか。状態遷移表さえ用意すればT4 templateが主要なコードを生成してくれ、残るアクションを追加するだけでアプリケーションが動き出します。生成されるコードが定型的であるほどコード生成のルール(テンプレート)を起こすのが楽ですし、一度テンプレートを起こしておけば何度でも使い回せます。今回は、テンプレート内に配列の形で用意した状態遷移表からC#/C++コードを生成しましたが、例えばHTML、XML、CSVなどなどを生成させることもできますし、アセンブリ(DLL)からメタ情報を抽出し加工することもできそうです。

 楽をするためなら苦労を厭わないのがプログラマの信条かと存じます。T4 templateで「コードを書く」コードを書いてみませんか?

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5783 2011/05/27 19:12

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー