Johan
Johan

Reputation: 76597

How do I assign a pre-existing function to a TComparison<T>?

program Project55;    
{$APPTYPE CONSOLE}
uses
  System.Generics.Defaults;

type
  TestRec<T> = record
    Compare: TComparison<T>;
    CompareI: IComparer<T>;
  end;

var
  TRI: TestRec<Integer>;

begin
  TRI.CompareI:= TComparer<Integer>.Default;
  TRI.Compare:= TRI.CompareI.Compare;  //E2035 Not enough actual parameters
  TRI.Compare:= @TRI.CompareI.Compare; //E2035 Not enough actual parameters
end.

I know I can assign the function body as an anonymous function, but why can't I assign an existing function?

Of course the following works, but that's just silly:

  TRI.Compare:= function(const L,R: integer): Integer
  begin
    Result:= TRI.CompareI.Compare(L,R);
  end;

PS. I'm using Delphi XE7, but I doubt the version matters.

Upvotes: 3

Views: 421

Answers (3)

David Heffernan
David Heffernan

Reputation: 613003

Your attempts to perform this assignment fail because an interface method cannot be with assigned to a method reference variable. The language simply does not permit that. The types are not assignment compatible. Valid assignment sources are anonymous methods, methods of classes (instance or class) and unit scope procedures.

The tricks that can be seen in other answers all depend on in depth knowledge of the implementation details. Which means that they are subject to change. But in terms of the language, what you are attempting is not permitted.

Upvotes: 3

Ken Bourassa
Ken Bourassa

Reputation: 6467

Anonymous methods are not exactly method pointers. They are implemented as an interface with a single method "Invoke".

It is possible to extract a method pointer from an anonymous method, but as far as I know it relies on the current implementation details of anonymous method and could be subject to changes in future version of delphi. In other words, I would advise against it. This was taken verbatim from Barry Kelly's post here. (Which covers the topic more thoroughly than I do here)

procedure MethRefToMethPtr(const MethRef; var MethPtr);
type
  TVtable = array[0..3] of Pointer;
  PVtable = ^TVtable;
  PPVtable = ^PVtable;
begin
  // 3 is offset of Invoke, after QI, AddRef, Release
  TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];
  TMethod(MethPtr).Data := Pointer(MethRef);
end;

Based on your example, I'd propose this as an alternative

type
  TestRec<T> = record
    CompareI: IComparer<T>;
    function Compare(const L, R : T) : Integer;
  end;
[...]
function TestRec<T>.Compare(const L, R : T) : Integer;
begin
  Result := CompareI.Compare(L,R);
end;

But then, it may/may not apply to your current situation.

Upvotes: 1

Stefan Glienke
Stefan Glienke

Reputation: 21713

Knowing that IComparer<T> is an interface with just one method that has the same signature as TComparison<T> and that anonymous methods are just interfaces with one method you can do the following.

IComparer<Integer>(TRI.Compare) := TRI.CompareI;

I am using that trick in Spring4D to avoid creating a wrapper object around a TComparison<T> to be passed as IComparer<T> because they are binary compatible.

Upvotes: 4

Related Questions