Delphiでコールバック

※ 環境はDelphi 6

JavaScriptを触ってると、Delphiでもコールバックが使いたくなってきた。
調べてみると、わりと簡単にできるようだ。

コールバック(英: Callback)とは、プログラミングにおいて、他のコードの引数として渡されるサブルーチンである。
コールバック (情報工学) - Wikipedia


コールバック関数は、呼び出し先で何らかのイベントが発生した場合の処理を指定する目的で使われることが多い。コールバック関数を使うと、イベントの発生を検知するための処理と、そのイベントが起こった時に実行すべき個々の処理を分離して記述することができる。
コールバック関数とは - IT用語辞典 e-Words

手続き(関数)を渡す場合

まず、手続き型を宣言する。引数が違えば、違う手続き型として宣言しなければならない。

type
    TProc = procedure;

実装例

procedure MyProc;
begin
    ShowMessage('This is MyProc');
end;

procedure CallProc(Proc: TProc);
begin
    Proc;
end;

procedure Main;

    procedure MyInnerProc;
    begin
        ShowMessage('This is MyInnerProc')
    end;

begin
    CallProc(MyProc);       // "This is MyProc"
    CallProc(@MyInnerProc); // "This is MyInnerProc"
end;

関数内関数はポインタにして渡す。(そのまま渡すとコンパイル時エラーになる)
また、親関数の変数を扱おうとすると実行時エラーになる。

メソッドを渡す場合

同様に手続き型を宣言するが、末尾に「of Object」を付加する。

type
    TStrProc = procedure(s: String) of Object;

    TMyClass = class(TObject)
    public
        greet: String;
        constructor Create(s: String);
        procedure MyProc(msg: String);
    end;

    THisClass = class(TObject)
    public
        procedure HisProc(CallBack: TStrProc);
    end;

実装例

constructor TMyClass.Create(s: String);
begin
    greet := s;
end;

procedure TMyClass.MyProc(msg: String);
begin
    ShowMessage(greet + msg);  // privateな変数にも触れる
end;

procedure THisClass.HisProc(CallBack: TStrProc);
begin
    if Assigned(CallBack) then  // <> nil ではなくAssignedを使う
        CallBack('This is Callback');
end;

procedure Main;
var
    Mine: TMyClass;
    His: THisClass;
begin
    Mine := TMyClass.Create('Hi, ');
    His := THisClass.Create;

    His.HisProc(Mine.MyProc);  // "Hi, This is Callback"

    His.Free;
    Mine.Free;
end;

関数内関数は型違いとしてコンパイル時エラーになる。(ポインタにしてもコンパイル時エラー)
もちろん、コールバックがよばれる前にインスタンスが解放されていると実行時エラーになる。
また、関数が割り当てられているかの判定にはAssignedを使う(func <> nil としない)。