4

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @J... That works in the actual arg is an anon method variable, but not if it is a method or function. – David Heffernan Jun 05 '15 at 13:57
  • If you are intending to pass both anonymous and regular functions to `ExternalIntegrand`, what does `GetAnonMethod` look like? Surely you need to know the type... a regular method pointer won't cast to `TFunc` - wouldn't it need to cast to some `TFoo = function(x:double):double;`? – J... Jun 05 '15 at 14:43
  • @J... I guess you are right. The function that calls `CalcIntegral` receives an anon method as an argument, so I've nailed the types down at that point. So, I'm not thinking this through. We can certainly add an untyped pointer version of `CastToPointer` but it's no less verbose. – David Heffernan Jun 05 '15 at 14:48
  • Or, if you are indeed certain to only pass anonymous methods, then I think my answer works without needing an extra method to do the casting (ie : accept an untyped parameter instead of a `Pointer`). – J... Jun 05 '15 at 14:50
  • @J... I'd have to change `CalcIntegral` to accept an untyped parameter. Then I agree it would fly. – David Heffernan Jun 05 '15 at 14:52
  • Or, if you need both anonymous and regular functions, a couple of overloads... – J... Jun 05 '15 at 15:21
  • What about `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 :) – Graymatter Jun 05 '15 at 19:36
  • @Graymatter Ha! That might do it. In typed address mode you might need to rejig the casts. – David Heffernan Jun 05 '15 at 19:43
  • Aesthetic is an odd thing - I didn't suggest this because it seemed exactly what you'd already done and didn't like (double indirection, multiple cast, etc). – J... Jun 05 '15 at 20:13
  • @J... I guess the difference between Graymatter's expression and mine is that his produces the pointer that I was looking for. Whereas I produced the address of that pointer. – David Heffernan Jun 05 '15 at 20:19
  • I guess it seemed trivial to me whether you dereferenced it before passing it into the function or after... I understood incorrectly that you were trying to get around the extra reference/dereference step altogether. I suppose it didn't cross my mind that you wouldn't have known this would work... I'm so used to you knowing everything! ;) – J... Jun 05 '15 at 20:22
  • @J... I just didn't think of it. I do think it's weird that anon method refs behave so differently from normal procedural types. – David Heffernan Jun 05 '15 at 20:29
  • It's definitely inelegant. That said, given Emba's design philosophy of building a compiler like an aftermarket tuner car, it doesn't strike me as particularly weird or out of character. – J... Jun 05 '15 at 21:09
  • @J... It's totally weird. With an anon method I'd write `@f`, with a method of object it's `@@f`. What gives?!! – David Heffernan Jun 05 '15 at 21:10
  • That was me agreeing with you in a rather roundabout way... – J... Jun 05 '15 at 21:11
  • 1
    @J... I know. That was me whining at somebody who appears to share my frustration! ;-) – David Heffernan Jun 05 '15 at 21:12

2 Answers2

5

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.
Graymatter
  • 6,529
  • 2
  • 30
  • 50
2

Not sure if this is what you mean, but this works if you can write your external method to accept an untyped parameter instead of a 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.

As noted, this does not function implicitly for regular procedures, but with an explicit assignment to TFunc you can also pass regular procedures.

output is

4.00000000000000E+0000

8.00000000000000E+0000

Naturally, if you need the flexibility of being able to pass both regular and anonymous methods, a few overloads add some nice type safety and clean syntax.

{$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.
J...
  • 30,968
  • 6
  • 66
  • 143
  • It doesn't work if you pass a function rather than an anonymous method. Define a module level function and try. What's more, there's no pointer here. I am calling a function that accepts a `Pointer`. I do need that. – David Heffernan Jun 05 '15 at 14:05
  • @DavidHeffernan Is it correct to presume, then, that you do not control the source for the external method (ie: it must accept an explicit `Pointer` type)? Also, the question was about anonymous methods - should the answer also address module level functions? – J... Jun 05 '15 at 14:06
  • I control it all in effect. But if I use an untyped param then I cannot pass a method or function, I have to pass an anon method. Because they are passed differently to an untyped param. Try it and see. – David Heffernan Jun 05 '15 at 14:11
  • @DavidHeffernan Ah, I see - the solution must also support regular methods, not *only* anonymous methods. – J... Jun 05 '15 at 14:12
  • They are assignment compatible with anonymous methods after all. I almost had an untyped pointer variant of `CastToPointer` in my question, but then I hit that stumbling block and removed it. Perhaps what all of this is telling me is that I should use `@F` where `F` is a variable of type anon method. – David Heffernan Jun 05 '15 at 14:14