DWScript:如何从Delphi侧读取元类参数

3

我在使用DWScript中遇到了元类(meta classes)的问题。

我们使用脚本编写使VAR和终端用户可以自定义我们的应用程序。

我们的应用数据基本上由许多小对象组成的树形结构。每个对象可以是“愚笨”的,只显示数据,也可以是智能的。通过将不同的脚本类与树对象关联来实现智能功能。

我遇到的问题是,脚本需要与Delphi侧框架通信,告诉它要使用什么脚本类来实现对象。基本上,我需要将脚本元类传递到Delphi侧,并以可以安全持久化的格式(可能是类型名称字符串)存储信息。我还需要能够沿着相反的方向进行操作;即从Delphi侧返回元类到脚本。

TdwsUnit声明

type
  // Base class of all tree objects
  TItem = class
    ...
  end;


  // The meta class
  // This is actually declared in code since TdwsUnit doesn't have design time support for meta classes.
  // Shown here for readability.
  TItemClass = class of TItem;


// The procedure that passes the meta class to the Delphi side.
// I cannot use a TItemClass parameter as that isn't declared until run time (after the TdwsUnit has initialized its tables).
procedure RegisterItemClass(AClass: TClass);

脚本

type
  TMyItem = class(TItem)
    ...
  end;

begin
  // Pass the meta class to the Delphi side.
  // The Delphi side will use this to create a script object of the specified type
  // and attach it to the Delphi side object.

  RegisterItemClass(TMyItem);
end;

Delphi实现

TItemClass是元类的声明,它在TdwsUnit.OnAfterInitUnitTable中完成。

procedure TMyDataModule.dwsUnitMyClassesAfterInitUnitTable(Sender: TObject);
var
  ItemClass: TClassSymbol;
  MetaClass: TClassOfSymbol;
begin
  // Find the base class symbol
  ItemClass := dwsUnitMyClasses.Table.FindTypeLocal('TItem') as TClassSymbol;
  // Create a meta class symbol
  MetaClass := TClassOfSymbol.Create('TItemClass', ItemClass);
  dwsUnitMyClasses.Table.AddSymbol(MetaClass);
end;

RegisterItemClass 实现

procedure TMyDataModule.dwsUnitMyClassesFunctionsRegisterItemClassEval(info: TProgramInfo);
var
  ItemClassSymbol: TSymbol;
  ItemClassName: string;
begin
  ItemClassSymbol := TSymbol(Info.Params[0].ValueAsInteger);
  ItemClassName := ItemClassSymbol.Name;
  ...
end;
问题是如何从元类参数获取TSymbol?
编辑:我在这个旧问题中找到了解决问题的一部分。
简而言之,解决方案是将参数值强制转换为TSymbol

然而...

现在假设我将类名存储为字符串。 如何从此类名返回符号? 我需要这样做,因为就像脚本可以设置项目类(使用上面的代码)一样,脚本也可以要求项的类。

我尝试使用任何四种不同方法中的符号表来查找我需要的符号,但没有一种能够找到该符号。

var
  ItemClassName: string;
  ItemClassSymbol: TSymbol;
...
  ItemClassName := 'TMyItem';
...
  ItemClassSymbol := Info.Table.FindTypeSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindTypeLocal(ItemClassName);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindLocal(ItemClassName);

  // ItemClassSymbol is nil at this point :-(

所以问题是:如何从Delphi端获取对应的TSymbol,给定在脚本中声明的元类名?
编辑:我现在已经找到了最后一部分的一个可能的解决方案。
以下内容似乎有效,但我不确定这是否是正确的方法。我原以为我需要将符号搜索范围限制在当前脚本单元内。
var
  ItemClassName: string;
  ItemClassSymbol: TSymbol;
...
  ItemClassName := 'TMyItem';
...
  ItemClassSymbol := Info.Execution.Prog.RootTable.FindSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    raise EScriptException.CreateFmt('ItemClass not found: %s', [ItemClassName]);

  Info.ResultAsInteger := Int64(ItemClassSymbol);
1个回答

2

除非我误解了,你可能不应该查找最后一部分的符号表,而是应该维护一个在dwsUnitMyClassesFunctionsRegisterItemClassEval中注册的注册项类表。

这样做的原因是用户可能有两个‘TMyItem’符号,在两个不同的上下文中,但只有一个被注册。已注册的那个是你想要的,我认为没有可靠的方法来确定相关符号(因为关键的上下文并不是你试图将字符串解析回符号的上下文,而是符号和字符串被关联的上下文,即它被注册的地方)。


我明白你的观点。我本来期望符号查找应该使用普通的作用域规则,所以同名类的任何后续声明都会覆盖先前的声明(当然前一个还在作用域内)。我无法在单元上维护符号表,因为该单元可以在许多不同的独立脚本之间共享,但我可能能够简单地将指向已存储类名的对象上的符号指针存储起来。不过我有些担心符号可能会在背后被删除,留下一个无效的引用。 - SpeedFreak
在创建TMyItem实例时似乎有些东西丢失了。由于无法通过正常的符号查找找到该符号,因此我无法使用通常的Info.Vars ['TMyItem'] .GetConstructor。相反,我不得不调用受保护的TProgramInfo.GetSymbolInfo来创建IInfo对象,并在其中调用构造函数(这反过来导致我遇到了这个错误)。 - SpeedFreak
你没有找到它,是因为正常的作用域规则。你试图查找的函数是单元的一部分,在那里用户类不在范围内,因此你必须返回程序的根表来查看它。如果想在程序级别拥有IInfo,可以将该根表传递给新的TProgramInfo.Table属性。至于列表,可以拥有每次执行的环境,在那里可以存储自定义内容(参见WebEnvironement的示例)。 - Eric Grange
我正在尝试从声明它的同一脚本中解析符号,因此我希望它在范围内。也许如果我制作一个能够重现问题并将其提交为错误的小样本会更好?我不明白你所说的“TProgramInfo.Table”。我已经在使用自定义环境来为脚本提供上下文,但我不认为这是在这种情况下存储符号信息的正确位置。我现在不在电脑前,但我回来后会解释原因。 - SpeedFreak
我以为你正在尝试从TdwsUnit中的函数解决问题?(如果是这样,你的代码处于TdwsUnit的上下文中,即它的实现位置,而不是调用该函数的单元)。无论如何,您可能希望在googlecode跟踪器中提交详细信息的问题,这比注释更方便。 - Eric Grange
我正在从声明为TdswUnit的函数中解析符号名称,因此将上下文传递给函数实现是有意义的。但是,有没有办法访问调用者范围(即脚本范围)呢?尽管这不是我最终会使用的解决方案,但我现在已经接受了你的答案,因为它似乎是一个可能的解决方案。 - SpeedFreak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接