在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将静态集表示为数组.但请注意,这只是编译器前端的限制,希望有一天能够解决.见DWScript issue #10: Improve implicit casts from static arrays to sets.
以下脚本演示了编译器在set和array之间执行隐式转换的情况:
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;
现在让我们尝试使用您的解决方案,将set参数声明为数组:
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.这样可以很好地工作并解决上面的所有情况,否则会失败.