11

私はスクリプトインタープリターに取り組んでおり、なんとか動作状態になりました。スクリプトを解析してバイトコードを生成するコンパイラと、バイトコードを実行する VM を備えています。

caseインタプリタの中心にあるのは、次のような巨大なステートメントを含むループです。

case CurrentOpcode.Operation of
  OP_1: DoOp1(CurrentOpcode);
  OP_2: DoOp2(CurrentOpcode);
  ...
  OP_N: DoOpN(CurrentOpcode);
end;

プロファイリングによると、何らかの理由で、スクリプトの実行がそのステートメント内でかなりの時間を費やしていることがわかりましたcase。これは奇妙に思えるので、それを最適化する方法を探しています。すべての操作関数は基本的に同じシグネチャを持っているため、明白な解決策は、オペコードのOperation値によってインデックス付けされたメソッド ポインターの配列を作成することです。しかしOperation、列挙型として宣言されており、これを const 配列として宣言できると便利です。これにより、将来さらにオペコードを追加した場合に、コンパイラが配列を更新するように通知することができます。

メソッド ポインターは実行時の状態 (実行Self中のオブジェクトへの参照) を格納するため、メソッド ポインターの const 配列を作成することはできません。(とにかく、これは良い考えではありません。同時に複数のスクリプトを実行することになる可能性が非常に高いためです。) しかし、とにかくメソッドは単なる構文糖衣です。何かのようなもの:

procedure TMyObject.DoSomething(x, y: integer);

本当に意味:

procedure TMyObject_DoSomething(Self: TMyObject; x, y: integer);

したがって、後者の形式で関数ポインター型を宣言し、そのように割り当てることができるはずです。その後、Self呼び出すときに最初のパラメーターとして明示的に渡す必要があります。しかし、コンパイラはそれを好まない。

type TOpcodeProc = procedure (Self: TScriptVM; Opcode: TOpcode);
const OPCODE: TOpcodeProc = TScriptVM.DoOp1;

[DCC Error]: E2009 Incompatible types: 'regular procedure and method pointer'

これをコンパイルするためにさまざまなバリエーションを試しましたが、すべてエラーが発生します。これをコンパイルする方法はありますか?

4

2 に答える 2

9

宣言:

const
  OPCODE: array[TOperation] of Pointer = (
    @TScriptVM.DoOp1, 
    @TScriptVM.DoOp2, 
    ... 
    @TScriptVM.DoOpN
  );

電話:

TOpcodeProc(OPCODE[CurrentOpcode.Operation])(Self, CurrentOpcode);

もっとクールなもの:

var
  OpCodeProcs: array[TOperation] of TOpCodeProc absolute OPCODE;

メソッドを呼び出すためのはるかに優れた構文:

OpCodeProcs[CurrentOpcode.Operation](Self, CurrentOpcode);

良いことは、コンパイラが OpCodeProcs 変数に何かを代入することを防止する定数への絶対値のためです!

于 2013-02-06T08:26:57.353 に答える
5

質問の一定の部分に対する解決策はありませんが、次のように削除できますcase

type
  TTestMethod = procedure(Instance: TObject; Param1, Param2: Integer);

  TTest = class(TObject)
  private
    FMethods: array[0..1] of TTestMethod;
    procedure InitMethods;
    procedure CallMethod(ID: Integer; Param1, Param2: Integer);
  protected
    procedure TestMethod0(Param1, Param2: Integer);
    procedure TestMethod1(Param1, Param2: Integer);
  end;

procedure TTest.InitMethods;
begin
  FMethods[0] := TTestMethod(@TTest.TestMethod0);
  FMethods[1] := TTestMethod(@TTest.TestMethod1);
end;

procedure TTest.CallMethod(ID: Integer; Param1, Param2: Integer);
begin
  FMethods[ID](Self, Param1, Param2);
end;
于 2013-02-05T23:27:44.343 に答える