【问题标题】:Is there a clean way to cast an anonymous method to a pointer?有没有一种干净的方法可以将匿名方法转换为指针?
【发布时间】:2015-06-05 13:28:07
【问题描述】:

我将匿名方法传递给外部函数。匿名方法是一个被积函数,外部函数将计算一个定积分。因为集成功能是外部的,所以它不理解匿名方法。所以我不得不将匿名方法作为无类型指针传递。为了更清楚,它运行如下:

function ExternalIntegrand(data: Pointer; x: Double): Double; cdecl;
begin
  Result := GetAnonMethod(data)(x);
end;

....

var
  Integrand: TFunc<Double,Double>;
  Integral: Double;
....
Integral := CalcIntegral(ExternalIntegrand, CastToPointer(Integrand), xlow, xhigh);

这里的CalcIntegral 是调用ExternalIntegrand 的外部函数。反过来,它获取传递的无类型指针,检索匿名方法,并让它完成这项工作。

问题是我不能干净地写CastToPointer。如果我这样做:

Pointer(Integrand)

编译器对象:

[dcc32 错误]: E2035 实际参数不足

显然编译器正在尝试调用匿名方法。

我可以做到:

function CastToPointer(const F: TFunc<Double,Double>): Pointer; inline;
begin
  Move(F, Result, SizeOf(Result));
end;

或者这个:

function CastToPointer(const F: TFunc<Double,Double>): Pointer; inline;
var
  P: Pointer absolute F;
begin
  Result := P;
end;

但是,我不能像在将动态数组转换为指向数组的指针时那样使用简单的转换,这似乎有点令人讨厌。

我意识到我可以传递保存匿名方法的变量的地址。像这样:

function ExternalIntegrand(data: Pointer; x: Double): Double; cdecl;
var
  F: ^TFunc<Double,Double>;
begin
  F := data;
  Result := F^(x);
end;

....

Integral := CalcIntegral(ExternalIntegrand, @Integrand, xlow, xhigh);

但是,必须引入另一个级别的间接性似乎有点奇怪。

有人知道将匿名方法变量直接转换为指针的方法吗?我确实意识到这种欺骗是值得怀疑的,但至少出于好奇,我想知道它是否可以做到。

【问题讨论】:

  • @J... 在实际 arg 中有效的是匿名方法变量,但如果它是方法或函数则无效。
  • 如果您打算将匿名函数和常规函数都传递给ExternalIntegrandGetAnonMethod 是什么样的?当然,您需要知道类型...常规方法指针不会转换为 TFunc&lt;double,double&gt; - 它不需要转换为 TFoo = function(x:double):double; 吗?
  • @J... 我猜你是对的。调用CalcIntegral 的函数接收一个匿名方法作为参数,所以我已经确定了类型。所以,我没有考虑清楚。我们当然可以添加CastToPointer 的无类型指针版本,但同样冗长。
  • 或者,如果您确实确定只传递匿名方法,那么我认为我的答案不需要额外的方法来进行转换(即:接受无类型参数而不是 Pointer) .
  • @J... 我知道。那是我对一个似乎和我一样沮丧的人发牢骚! ;-)

标签: delphi delphi-xe7


【解决方案1】:

你应该可以只做Pointer((@Integrand)^),所以你的电话是:

Integral := CalcIntegral(ExternalIntegrand, Pointer((@Integrand)^), xlow, xhigh);

这是一种额外的间接级别,但不是:)

我通过与您的 CastToPointer 进行比较进行了测试,它有效:

program Project8;

{$APPTYPE CONSOLE}

{$R *.res}

{$T+}

uses
  System.SysUtils;

  function CastToPointer(const F: TFunc<Double,Double>): Pointer; inline;
begin
  Move(F, Result, SizeOf(Result));
end;

var
  Integrand: TFunc<Double,Double>;
  Mypointer1: Pointer;
  Mypointer2: Pointer;
begin
  Integrand := function(x : double) : double
       begin
         result := 2 * x;
       end;
  Mypointer1 := Pointer((@Integrand)^);
  Mypointer2 := CastToPointer(Integrand);
  Assert(Mypointer1 = Mypointer2, 'Pointers don''t match!');
end.

【讨论】:

    【解决方案2】:

    不确定这是否是您的意思,但如果您可以编写外部方法来接受无类型参数而不是Pointer,则此方法有效。

    {$APPTYPE CONSOLE}
    
    uses
      SysUtils;
    
    function Foo(x : double) : double;
    begin
      result := 4 * x;
    end;
    
    procedure Test2(const data);
    begin
      WriteLn(TFunc<Double,Double>(data)(2));
    end;
    
    var
      F: TFunc<Double,Double>;
    begin
      F := function(x : double) : double
           begin
             result := 2 * x;
           end;
      Test2(F);  // Anonymous method
      F := foo;
      Test2(F);  // Regular method
      ReadLn;
    end.
    

    如上所述,这对于常规过程不会隐式起作用,但通过显式分配给TFunc,您也可以传递常规过程。

    输出是

    4.00000000000000E+0000

    8.00000000000000E+0000

    当然,如果您需要能够同时传递常规方法和匿名方法的灵活性,一些重载会增加一些不错的类型安全性和简洁的语法。

    {$APPTYPE CONSOLE}
    
    uses
      SysUtils;
    
    type TFoo = function(x : double) : double;
    
    function Foo(x : double) : double;
    begin
      result := 4*x;
    end;
    
    function Test2(const data) : double;
    begin
      result := TFunc<Double,Double>(data)(2);
    end;
    
    function Test(data : TFoo) : double; overload;
    var
      F : TFunc<double,double>;
    begin
      F := data;
      result := Test2(F);
    end;
    
    function Test(data : TFunc<Double,Double>) : double; overload;
    begin
      result := Test2(data);
    end;
    
    var
      F: TFunc<Double,Double>;
    begin
      F := function(x:double):double
           begin
             result := 2*x;
           end;
      WriteLn(Test(F));  // Anonymous method
      WriteLn(Test(foo));  // Regular method
      ReadLn;
    end.
    

    【讨论】:

    • 如果你传递一个函数而不是一个匿名方法,它就不起作用了。定义一个模块级函数并尝试。更重要的是,这里没有指针。我正在调用一个接受Pointer 的函数。我确实需要那个。
    • @DavidHeffernan 那么假设您不控制外部方法的源是否正确(即:它必须接受显式的Pointer 类型)?另外,问题是关于匿名方法的——答案是否也应该涉及模块级函数?
    • 我有效地控制了这一切。但是如果我使用一个无类型的参数,那么我就不能传递一个方法或函数,我必须传递一个匿名方法。因为它们以不同的方式传递给无类型参数。试试看。
    • @DavidHeffernan 啊,我明白了 - 解决方案还必须支持常规方法,而不是 only 匿名方法。
    • 它们毕竟是与匿名方法兼容的赋值。在我的问题中,我几乎有一个CastToPointer 的无类型指针变体,但后来我碰到了那个绊脚石并将其删除。也许这一切都在告诉我,我应该使用@F,其中F 是匿名方法类型的变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 2020-02-26
    • 2015-03-08
    • 2018-06-30
    • 1970-01-01
    • 2021-11-13
    相关资源
    最近更新 更多