11

我正在开发一个脚本解释器,我已经设法进入工作状态。它有一个解析脚本并生成字节码的编译器,以及一个执行字节码的虚拟机。

解释器的核心是一个带有巨大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 回答