Есть ли чистый способ привести анонимный метод к указателю?


Я передаю анонимный метод внешней функции. Анонимный метод является подынтегральным, а внешняя функция вычисляет определенный интеграл. Поскольку функция интеграции является внешней, она не понимает анонимных методов. Поэтому мне приходится передавать анонимный метод как нетипизированный указатель. Чтобы сделать это более ясным, он работает следующим образом:

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);
Тем не менее, кажется немного странным вводить еще один уровень косвенности.

Кто-нибудь знает способ привести переменную анонимного метода непосредственно к указателю? Я понимаю, что такое надувательство сомнительно, но, по крайней мере, из любопытства я хотел бы знать, можно ли это сделать.

2 4

2 ответа:

Вы должны быть в состоянии просто сделать 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.

Не уверен, что это то, что вы имеете в виду, но это работает, если вы можете написать свой внешний метод, чтобы принять нетипизированный параметр вместо 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.00000000000000 E+0000

8.00000000000000 E+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.