有没有一种简洁的方法将匿名方法转换为指针?

4

我正在将一个匿名方法传递给外部函数。这个匿名方法是被积函数,而外部函数将计算一个定积分。由于积分函数是外部的,它不理解匿名方法。因此,我必须将匿名方法作为未经类型化的指针传递。为了更清楚地说明,它的运行方式如下:

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

然而,引入另一层间接性似乎有点奇怪。

有人知道将匿名方法变量直接转换为指针的方法吗?我知道这样的做法是可疑的,但出于好奇至少想知道是否可以这样做。


@J... 如果实际参数是匿名方法变量,则可以工作,但如果是方法或函数则不行。 - David Heffernan
如果你想同时传递匿名函数和常规函数给ExternalIntegrand,那么GetAnonMethod会是什么样子呢?你肯定需要知道类型...普通的方法指针无法强制转换为TFunc<double,double> - 难道它不需要强制转换为一些 TFoo = function(x:double):double; 吗? - J...
@J... 我想你是对的。调用 CalcIntegral 的函数将匿名方法作为参数接收,因此我已经在那一点上确定了类型。所以,我没有好好思考这个问题。我们当然可以添加一个未指定类型的指针版本的 CastToPointer,但它并不会更简洁。 - David Heffernan
或者,如果您需要匿名函数和常规函数,可以使用几个重载... - J...
1
@J...我知道。那是我对一个似乎与我有同样的沮丧感叹的人发牢骚!;-) - David Heffernan
显示剩余11条评论
2个回答

5

您只需要执行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

不确定这是否符合你的意思,但如果你可以编写一个接受未类型化参数而不是指针的外部方法,则此方法可行。

{$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的函数。我确实需要它。 - David Heffernan
@DavidHeffernan 那么,可以假设您无法控制外部方法的源代码(即:它必须接受显式的“Pointer”类型),对吗?此外,问题是关于匿名方法的 - 答案是否也应该涉及模块级函数? - J...
我实际上控制着所有的东西。但是,如果我使用未经类型化的参数,则无法传递方法或函数,我必须传递一个匿名方法。因为它们与未经类型化的参数传递方式不同。试一下就知道了。 - David Heffernan
@DavidHeffernan 哦,我明白了 - 解决方案还必须支持常规方法,而不仅仅是匿名方法。 - J...
毕竟它们与匿名方法是赋值兼容的。我在我的问题中几乎使用了一个未经类型化的指针变体CastToPointer,但后来我遇到了这个障碍并将其删除。也许所有这些都告诉我应该在F是类型为匿名方法的变量时使用@F - David Heffernan

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