DWScript:传递一组枚举类型将传递一个整数数组
DWScript: passing a set-of-enumerated-type will pass an array of integer
从 DWScript 脚本中,我调用了 Delphi 端公开的对象实例的方法。除其他外,该方法采用一个参数,该参数是一些枚举数据类型的集合。此枚举数据类型从 Delphi 公开给脚本。
我从脚本编译时生成的错误消息中看到,DWScript 将这样的参数作为整数数组传递,并且 Delphi 端接收到变量数组 (TData)。
我不得不在 Delphi 端编写一个包装器,它循环遍历数组并重建相应的变量集以将其传递给实际的 Delphi 函数。使用 "ProgramInfo.Vars['MsgFlags'].GetData".
访问数组
这非常有效,但这样做是否正确?我错过了什么吗?
脚本端代码:
procedure Test;
begin
DelphiObject.Demo('Hello', [mffStop, mffClose]);
end;
Delphi 边码:
TFlag = (mmfStop, mffStart, mmfClose);
TFlags = set of TFlag;
// Internal method doing the actual job
procedure TDelphiObject.DemoInternal(
const MsgText : String;
const MsgFlags : TFlags);
begin
// Some code...
end;
// Wrapper method exposed to script
procedure TDelphiObject.Demo(
const MsgText : String;
const MsgFlags : array of integer);
var
Flags : TFlags;
I : Integer;
begin
Flags := [];
for I := Low(MsgFlags) to High(MsgFlags) do
Flags := Flags + [TFlag(MsgFlags[I])];
DemoInternal(MsgText, Flags);
end;
我会以不同的方式实现 Delphi 方面(见下文),但除此之外,您的解决方案似乎是正确的。
正如您正确观察到的那样,怪癖是 DWScript 将 static sets 表示为数组。但是请注意,这只是编译器前端的一个限制,希望有一天会得到解决。参见 DWScript issue #10: Improve implicit casts from static arrays to sets。
以下脚本演示了编译器在哪些情况下在集合和数组之间执行隐式转换:
type
TMyEnum = (meOne, meTwo);
type
TMySet = set of TMyEnum;
type
TMyArray = array of TMyEnum;
procedure TestSet(MySet: TMySet);
begin
ShowMessage(integer(MySet).toString);
end;
procedure TestArray(MyArray: TMyArray);
var
MySet: TMySet;
begin
MySet := [];
for var i := 0 to MyArray.Length-1 do
Include(MySet, MyArray[i]);
ShowMessage(integer(MySet).toString);
end;
begin
TestSet([]);
TestArray([]);
TestSet([meOne]);
TestArray([meOne]);
TestSet([meOne, meTwo]);
TestArray([meOne, meTwo]);
var VarSet: TMySet = [meOne, meTwo];
TestSet(VarSet);
// Syntax Error: Argument 0 expects type "array of TMyEnum" instead of "TMySet"
// TestArray(VarSet);
var VarArray: TMyArray = [meOne, meTwo];
TestArray(VarArray);
// Syntax Error: Argument 0 expects type "array of TMyEnum" instead of "TMySet"
// TestArray(VarSet);
// Syntax Error: Incompatible types: "TMySet" and "array [0..1] of TMyEnum" const ConstSet: TMySet = [meOne, meTwo];
// const ConstSet: TMySet = [meOne, meTwo];
// TestSet(ConstSet);
// TestArray(ConstSet);
// Syntax Error: Incompatible types: "array of TMyEnum" and "array [0..1] of TMyEnum"
// const ConstArray: TMyArray = [meOne, meTwo];
// TestSet(ConstArray);
// TestArray(ConstArray);
end;
以上纯属脚本端实现。当您将 Delphi 边实现添加到混合中时,它可能会出现问题。
考虑 MessageDlg
函数的简化实现:
Delphi 边声明(通过 TdwsUnit):
type
TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel, etc...);
TMsgDlgButtons = set of TMsgDlgBtn;
function MessageDlg(const Msg: string; Buttons: TMsgDlgButtons): integer;
Delphi 端实现:
Info.ResultAsInteger := MessageDlg(Info.ParamAsString[0], mtInformation, TMsgDlgButtons(Word(Info.ParamAsInteger[1])), -1);
脚本端用法:
begin
// Implicit cast from array to set fails:
// Syntax Error: There is no overloaded version of "MessageDlg" that can be called with these arguments
// MessageDlg('Test', [mbOK]);
var Buttons: TMsgDlgButtons = [mbOK];
MessageDlg('Test', Buttons);
end;
现在让我们尝试使用您的解决方案,将设置参数声明为数组:
Delphi 边声明(通过 TdwsUnit):
type
TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel, etc...);
TMsgDlgButtons = array of TMsgDlgBtn;
function MessageDlg(const Msg: string; Buttons: TMsgDlgButtons): integer;
Delphi 端实现:
var
Buttons: TMsgDlgButtons;
i: integer;
ButtonArray: IScriptDynArray;
begin
ButtonArray := Info.Params[1].ScriptDynArray;
Buttons := [];
for i := 0 to ButtonArray.ArrayLength-1 do
Include(Buttons, TMsgDlgBtn(ButtonArray.AsInteger[i]));
Info.ResultAsInteger := MessageDlgEx(Info.ParamAsString[0], mtInformation, Buttons, -1);
end;
脚本端用法:
begin
MessageDlg('Test', [mbOK]);
var Buttons: TMsgDlgButtons = [mbOK];
// Note that an implicit cast from set to array is performed
MessageDlg('Test', Buttons);
end;
在我自己的 DWScript 分支中,我修改了编译器以执行从枚举值数组到集合的隐式转换:DWScript pull request #4: Enhancement to set type。这很好地工作并解决了上述所有失败的情况。
从 DWScript 脚本中,我调用了 Delphi 端公开的对象实例的方法。除其他外,该方法采用一个参数,该参数是一些枚举数据类型的集合。此枚举数据类型从 Delphi 公开给脚本。
我从脚本编译时生成的错误消息中看到,DWScript 将这样的参数作为整数数组传递,并且 Delphi 端接收到变量数组 (TData)。
我不得不在 Delphi 端编写一个包装器,它循环遍历数组并重建相应的变量集以将其传递给实际的 Delphi 函数。使用 "ProgramInfo.Vars['MsgFlags'].GetData".
访问数组这非常有效,但这样做是否正确?我错过了什么吗?
脚本端代码:
procedure Test;
begin
DelphiObject.Demo('Hello', [mffStop, mffClose]);
end;
Delphi 边码:
TFlag = (mmfStop, mffStart, mmfClose);
TFlags = set of TFlag;
// Internal method doing the actual job
procedure TDelphiObject.DemoInternal(
const MsgText : String;
const MsgFlags : TFlags);
begin
// Some code...
end;
// Wrapper method exposed to script
procedure TDelphiObject.Demo(
const MsgText : String;
const MsgFlags : array of integer);
var
Flags : TFlags;
I : Integer;
begin
Flags := [];
for I := Low(MsgFlags) to High(MsgFlags) do
Flags := Flags + [TFlag(MsgFlags[I])];
DemoInternal(MsgText, Flags);
end;
我会以不同的方式实现 Delphi 方面(见下文),但除此之外,您的解决方案似乎是正确的。
正如您正确观察到的那样,怪癖是 DWScript 将 static sets 表示为数组。但是请注意,这只是编译器前端的一个限制,希望有一天会得到解决。参见 DWScript issue #10: Improve implicit casts from static arrays to sets。
以下脚本演示了编译器在哪些情况下在集合和数组之间执行隐式转换:
type
TMyEnum = (meOne, meTwo);
type
TMySet = set of TMyEnum;
type
TMyArray = array of TMyEnum;
procedure TestSet(MySet: TMySet);
begin
ShowMessage(integer(MySet).toString);
end;
procedure TestArray(MyArray: TMyArray);
var
MySet: TMySet;
begin
MySet := [];
for var i := 0 to MyArray.Length-1 do
Include(MySet, MyArray[i]);
ShowMessage(integer(MySet).toString);
end;
begin
TestSet([]);
TestArray([]);
TestSet([meOne]);
TestArray([meOne]);
TestSet([meOne, meTwo]);
TestArray([meOne, meTwo]);
var VarSet: TMySet = [meOne, meTwo];
TestSet(VarSet);
// Syntax Error: Argument 0 expects type "array of TMyEnum" instead of "TMySet"
// TestArray(VarSet);
var VarArray: TMyArray = [meOne, meTwo];
TestArray(VarArray);
// Syntax Error: Argument 0 expects type "array of TMyEnum" instead of "TMySet"
// TestArray(VarSet);
// Syntax Error: Incompatible types: "TMySet" and "array [0..1] of TMyEnum" const ConstSet: TMySet = [meOne, meTwo];
// const ConstSet: TMySet = [meOne, meTwo];
// TestSet(ConstSet);
// TestArray(ConstSet);
// Syntax Error: Incompatible types: "array of TMyEnum" and "array [0..1] of TMyEnum"
// const ConstArray: TMyArray = [meOne, meTwo];
// TestSet(ConstArray);
// TestArray(ConstArray);
end;
以上纯属脚本端实现。当您将 Delphi 边实现添加到混合中时,它可能会出现问题。
考虑 MessageDlg
函数的简化实现:
Delphi 边声明(通过 TdwsUnit):
type
TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel, etc...);
TMsgDlgButtons = set of TMsgDlgBtn;
function MessageDlg(const Msg: string; Buttons: TMsgDlgButtons): integer;
Delphi 端实现:
Info.ResultAsInteger := MessageDlg(Info.ParamAsString[0], mtInformation, TMsgDlgButtons(Word(Info.ParamAsInteger[1])), -1);
脚本端用法:
begin
// Implicit cast from array to set fails:
// Syntax Error: There is no overloaded version of "MessageDlg" that can be called with these arguments
// MessageDlg('Test', [mbOK]);
var Buttons: TMsgDlgButtons = [mbOK];
MessageDlg('Test', Buttons);
end;
现在让我们尝试使用您的解决方案,将设置参数声明为数组:
Delphi 边声明(通过 TdwsUnit):
type
TMsgDlgBtn = (mbYes, mbNo, mbOK, mbCancel, etc...);
TMsgDlgButtons = array of TMsgDlgBtn;
function MessageDlg(const Msg: string; Buttons: TMsgDlgButtons): integer;
Delphi 端实现:
var
Buttons: TMsgDlgButtons;
i: integer;
ButtonArray: IScriptDynArray;
begin
ButtonArray := Info.Params[1].ScriptDynArray;
Buttons := [];
for i := 0 to ButtonArray.ArrayLength-1 do
Include(Buttons, TMsgDlgBtn(ButtonArray.AsInteger[i]));
Info.ResultAsInteger := MessageDlgEx(Info.ParamAsString[0], mtInformation, Buttons, -1);
end;
脚本端用法:
begin
MessageDlg('Test', [mbOK]);
var Buttons: TMsgDlgButtons = [mbOK];
// Note that an implicit cast from set to array is performed
MessageDlg('Test', Buttons);
end;
在我自己的 DWScript 分支中,我修改了编译器以执行从枚举值数组到集合的隐式转换:DWScript pull request #4: Enhancement to set type。这很好地工作并解决了上述所有失败的情况。