【问题标题】:Fast way to identify the location the current procedure was called from识别调用当前过程的位置的快速方法
【发布时间】:2015-06-01 16:30:24
【问题描述】:

我需要找到一种方法来识别调用类中当前函数或过程的位置。结果是内存位置还是单元和行号无关紧要,只要是调用来自的唯一位置即可。

位置 ID 需要快速计算,因为它将用于决定缓存数据是否可用。

例如。

type
  TTestObject = class
  public
    procedure TestProc;
    procedure TestCall;
  end;  

...

procedure TTestObject.TestProc;
begin
  TestCall; // "Point A" - Displays "Point A"
  TestCall; // "Point B" - Displays "Point B"
end;

procedure TTestObject.TestCall;
begin
  ShowMessage(SomehowGetTheCallingLineLocation); // Displays "Point A" or "Point B" depending on which line above it is called from
end;

end.

在调用TestProc时会显示“点A”,然后显示“点B”,无论创建了多少个TTestObject实例或它们驻留在内存中的什么位置。

该功能将用于生成 SQL。目前,我将 GUID 传递给生成 SQL 的调用。如果 SQL 已经生成,则此 GUID 用于从缓存中拉取 SQL。

  NewCommand(NewUpdateCriteria('{C43D3B79-9E73-4A4B-9E29-0553542AD0B2}').
    SetValue('AFIELD', AValue).
  Table
    ('ATABLE').
  Where
    (NewSQLComparitor
    ('ID', EqualTo, AID)));

调用位置的查找需要很快,否则它会抵消我们应该通过缓存 SQL 看到的速度改进。

该组件最终可能会开源,因此我无法使用任何商业第三方组件。我还想避免对 JEDI 等开源库的依赖。

【问题讨论】:

  • 为什么不直接传入一个参数呢? TTestObject.TestCall(UseCached : boolean); 或者,任何存储缓存数据的对象都可以跟踪它是否具有有效数据。即:if FCacheDataStore.HasValidData then...
  • 我也想知道你所说的“快”是什么意思。您是否有特定的性能限制?
  • @DavidHeffernan - 我添加了一些关于我正在尝试做的事情的更多信息。
  • 顺便说一句,ORM 最终可能会开源,所以我无法使用任何商业第三方组件。我还想避免对 JEDI 等开源库的依赖。
  • @DavidHeffernan - 好的,我已将评论添加到问题中。谢谢。

标签: delphi delphi-xe7


【解决方案1】:

根据您更新的问题,您只需能够确定一个呼叫来自与另一个呼叫不同的地方。在这种情况下,您可以使用返回地址来识别调用者。并且返回地址可以通过调用未公开的内部函数System.ReturnAddress获得。

这个程序:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

procedure Foo;
begin
  Writeln(IntToHex(NativeInt(ReturnAddress), 8));
end;

begin
  Foo;
  Foo;
  Foo;
  Foo;
end.

在我的机器上产生这个输出:

0041B491 0041B496 0041B49B 0041B4A0

【讨论】:

  • +1 因为这直接有效地回答了这个问题,并且加倍因为我学到了一些新的东西。也就是说,对于 OP 的问题,这感觉像是一个非常糟糕的解决方案——考虑到要求,这种级别的代码混淆似乎完全不合理。将信息获取到方法中的常规方法是将其作为参数传递。我看不出这在这里行不通的充分理由。除非正在编写异常处理程序或类似的东西,否则基于方法的返回地址的条件分支就像一场噩梦......
  • @J... 我同意。这感觉就像是复杂和混淆,几乎没有明显的收益。
  • 另外,如果检查返回地址的方法有效,那么可以证明在编译时知道正确的分支。如果最大化性能(按构建查询字符串所需的时间顺序)是目标,那么简单地编写单独的方法会更快(完全删除比较和分支)。从一条线路拨打Foo;,从另一条线路拨打FooCached;
  • @J...-在负载很重的服务器上构建复杂的 SQL 可能会很耗时。这个想法是允许在第一次需要时构建 SQL,然后在后续执行时从缓存中加载。知道 SQL 的构建位置允许我们缓存它,而无需传递额外的参数或 GUID。我认为这不会混淆代码,但允许我们将自动缓存逻辑完全包含在 SQL 生成类中。
  • @norgepaul 我理解动机,我只是拒绝相信没有更好的方法可以实现您的目标。如果我在数据库库中找到此类代码,我会非常有动力尽快消灭它。这只是解决此类问题的一种糟糕方法......
猜你喜欢
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 2021-08-11
  • 1970-01-01
相关资源
最近更新 更多