Есть ли чистый способ привести анонимный метод к указателю?
Я передаю анонимный метод внешней функции. Анонимный метод является подынтегральным, а внешняя функция вычисляет определенный интеграл. Поскольку функция интеграции является внешней, она не понимает анонимных методов. Поэтому мне приходится передавать анонимный метод как нетипизированный указатель. Чтобы сделать это более ясным, он работает следующим образом:
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 ответа:
Вы должны быть в состоянии просто сделать
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.