有没有一种干净的方法可以将匿名方法转换为指针?

Is there a clean way to cast an anonymous method to a pointer?

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

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 Error]: E2035 Not enough actual parameters

很明显,编译器正在尝试调用匿名方法。

我能做到:

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);

然而,不得不引入另一层间接寻址似乎有点奇怪。

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

不确定这是否是您的意思,但如果您可以编写外部方法来接受无类型参数而不是 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((@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.