Delphi
DelphiDay 1

ヤター!気持ち悪いのできたよー!

More than 1 year has passed since last update.

ヤター!気持ち悪いのできたよー!

これは Delphi Advent Calendar 1日目の記事です。
先日、FMX のソースを見ていると面白いコードがあることに気がつきました。
↓こんなコードです。

function SwitchCase(const A: Integer): Integer;
label
  Case0, Case1, CaseElse;
begin
  Result := 0;

  case A of
    0: goto Case0;
    1: goto Case1;
    else goto CaseElse;
  end;

  Case0:
    Inc(Result);
  Case1:
    Inc(Result);
  CaseElse:
    Inc(Result);
end;

label を利用して C-Style な switch-case 文を実現しています。
case 文からわざわざ label に飛ばして飛んだ所から下に向かって実行していくというものです。

また、先日、こんなコードも書きました。

三項演算子が (ちょっと) 羨ましい Delphi ユーザーの集い

演算子オーバーロードを使って、擬似的に C の3項演算子を実現しています。

また、Quality Central に case 文に文字列を使えるようにして!という要望を出していました。
(QC に繋がらないのでリンクは割愛)

さて、ここで、ひらめきました!
演算子オーバーロードを使ったら、型に縛られない case 文作れるんじゃね!?
そして出来上がったのがコチラになります。

Switches.pas

使い方は非常に簡単です。

Swtich<型>(セレクタ)
  or 値 - (procedure begin 実行したい内容 end)
  or 値 - (procedure begin 実行したい内容 end)
  :
  :

と書けば通常の case 文同様、セレクタと値が一致した時に無名関数が実行されます。

たとえば、文字列の場合

SwitchStr('hello')
  or 'foo' - (procedure
  begin
    Writeln('Foo !');
  end)

  or 'hello' - (procedure
  begin
    Writeln('hello, world!'); // これが出力されます
  end)

  or 'bar' - (procedure
  begin
    Writeln('Bar !');
  end)

更に、比較関数を独自に定義できます。

SwitchStr('bar')
  .SetComparer( // 比較関数を定義します。ここでは全て大文字に変換して比較するようにします
    function(const L, R: String): Boolean
    begin
      Result := L.ToUpper = R.ToUpper
    end
  )

  or 'FOO' - (procedure
  begin
    Writeln('FOO');
  end)

  or 'BAR' - (procedure // 大文字ですが、定義した比較関数によってここがマッチします。
  begin
    Writeln('BAR');
  end)

  or 'BUZ' - (procedure
  begin
    Writeln('BAZ');
  end);

さらに、case 文同様、複数の値を書けたり、変数や関数も使用できます。

SwitchStr('hello')
  // 複数の値を列挙できます
  or '012'
  or '34567'
  or '89' - (procedure
  begin
    Writeln('Numbers');
  end)

  // 変数や関数も使用できます。
  or (function: String begin Result := 'Bar' end)()
  or sLineBreak - (procedure
  begin
    Writeln('Variable & Function');
  end);

さらに! C言語風の switch-case 文も実現できます!

Switch<Single>(1.0).CStyle // CStyle メソッドを呼び出すと C言語の switch-case のような動作になります
  or 1.0 - (procedure
  begin
    Writeln('1.0');       // 実行されます
  end)

  or 1.1 - (procedure
  begin
    Writeln('1.1');       // 実行されます
    Switch<Single>.Break; // C言語同様 Break 文を呼び出すと抜けます
  end)

  or 1.2 - (procedure
  begin
    Writeln('1.2');
  end);

と、まあこんなのを作って悦に入っていたのですが…

Delフサさんが、ほぼ同じ物を4年も前に作成されておりまして…

String可能なCase文

でも、いまさら、内容を変更できないので、そのまま記事にした次第であります…
oh.../(^o^)\

プログラム的には、Operator を使って一個の式にしているだけです。
or や - の戻り値を自分自身の型にすることで、or - or - と何個も繋げられるようにしています。戻り値が同じなので or or or という接続もできます。
変数は全てスタティック変数なので、メモリのオーバーヘッドはほとんど無いはずです。

また、Compare メソッドの中で使っている GetTypeKind や SizeOf を使ってコンパイル時解決する部分は、Lynaたんさんの全力わはーの記事が参考になりました!

operator を使うと気持ち悪いのが作れて楽しいですね!!
なお、今回のコードは結構本気で使えるんじゃないかと思っているのですが、いかがでしょう?