我正在编写一个脚本解释器,我已经设法进入工作状态.它有一个解析脚本并生成字节码的编译器,以及一个执行字节码的VM.
解释器的核心是一个带有巨大case语句的循环,看起来像这样:
case CurrentOpcode.Operation of
OP_1: DoOp1(CurrentOpcode);
OP_2: DoOp2(CurrentOpcode);
...
OP_N: DoOpN(CurrentOpcode);
end;
Run Code Online (Sandbox Code Playgroud)
分析告诉我,无论出于何种原因,我的脚本执行在该case语句中花费了大量时间,这对我来说似乎很奇怪,所以我正在寻找一种优化它的方法.显而易见的解决方案,因为所有操作函数基本上具有相同的签名,就是创建一个由操作码的Operation值索引的方法指针数组.但是Operation被声明为枚举,并且能够将其声明为const数组会很好,这样如果我将来添加更多的操作码,编译器可以提醒我更新数组.
由于方法指针存储运行时状态(Self对其运行的对象的引用),因此我无法创建方法指针的const数组.(无论如何,这也不是一个好主意,因为很可能我最终会同时运行多个脚本.)但方法只是语法糖.就像是:
procedure TMyObject.DoSomething(x, y: integer);
Run Code Online (Sandbox Code Playgroud)
真正意思:
procedure TMyObject_DoSomething(Self: TMyObject; x, y: integer);
Run Code Online (Sandbox Code Playgroud)
所以我应该能够在后一种形式中声明一个函数指针类型并以这种方式分配它,然后我只需要Self在调用它时作为第一个参数显式传递.但编译器并不喜欢这样.
type TOpcodeProc = procedure (Self: TScriptVM; Opcode: TOpcode);
const OPCODE: TOpcodeProc = TScriptVM.DoOp1;
[DCC Error]: E2009 Incompatible types: 'regular procedure and method pointer'
Run Code Online (Sandbox Code Playgroud)
我已经尝试了不同的变体来尝试让它编译,但它们都会给出错误.有没有办法让这个编译?
宣言:
const
OPCODE: array[TOperation] of Pointer = (
@TScriptVM.DoOp1,
@TScriptVM.DoOp2,
...
@TScriptVM.DoOpN
);
Run Code Online (Sandbox Code Playgroud)
呼叫:
TOpcodeProc(OPCODE[CurrentOpcode.Operation])(Self, CurrentOpcode);
Run Code Online (Sandbox Code Playgroud)
更酷的东西:
var
OpCodeProcs: array[TOperation] of TOpCodeProc absolute OPCODE;
Run Code Online (Sandbox Code Playgroud)
调用该方法的语法更好:
OpCodeProcs[CurrentOpcode.Operation](Self, CurrentOpcode);
Run Code Online (Sandbox Code Playgroud)
好的是因为编译器阻止你为OpCodeProcs变量赋值而绝对不变!
对于问题的不变部分没有解决方案,但这是你可以如何消除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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
301 次 |
| 最近记录: |