Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm passing an anonymous method to an external function. The anonymous method is an integrand and the external function will calculate a definite integral. Because the integration function is external it does not understand anonymous methods. So I'm having to pass the anonymous method as an untyped pointer. To make this clearer, it runs like this:

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

Here CalcIntegral is the external function that will call ExternalIntegrand. That in turn takes the untyped pointer that is passed on, retrieves the anonymous method, and gets it to do that work.

The problem is that I can't write CastToPointer cleanly. If I do:

Pointer(Integrand)

the compiler objects with:

[dcc32 Error]: E2035 Not enough actual parameters

Clearly the compiler is trying to call the anonymous method.

I am able to do this:

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

or this:

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

But it seems somewhat galling that I cannot use a simple cast as I might when casting a dynamic array to a pointer to the array.

I realise that I could pass the address of the variable holding the anonymous method. Like this:

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

However, it seems a little odd to have to introduce another level of indirection.

Does anybody know a way to cast an anonymous method variable directly to a pointer? I do realise that such skullduggery is questionable but at least out of curiosity I'd like to know if it can be done.

like image 700
David Heffernan Avatar asked Mar 16 '23 20:03

David Heffernan


1 Answers

You should be able to just do Pointer((@Integrand)^) so your call would be:

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

It's sort of an extra level of indirection but not :)

I tested by comparing to your CastToPointer and it works:

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.
like image 114
Graymatter Avatar answered Mar 25 '23 03:03

Graymatter