The following code (constructed only to demonstrate the problem) compiles and work开发者_运维百科s in Delphi 2010. In Delphi 2009, compiler fails with "E2035 Not enough actual parameters".
program Project50;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyProc = reference to procedure(param: integer);
var
a: TProc;
b: TMyProc;
begin
b := procedure (param: integer)
begin
end;
a := TProc(b); // <-- [DCC Error] Project50.dpr(19): E2035 Not enough actual parameters
end.
I have found only one very ugly hack to work around the problem (a: TProc absolute b). Does anybody knows of a nicer workaround for this compiler deficiency?
[TProc field is actually hidden inside a record that can store various 'executable' code - TProcedure, TMethod and TProc. Casting is used to store specific anonymous proc into this field.]
The trick is not to do
a := TProc(b);
but
TMyProc(a) := b;
That compiles and works in D2009. Sample project attached below.
program Project51;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyProc = reference to procedure(var param: integer);
TStorage = record
FDelegate: TProc;
end;
var
a : TMyProc;
b : TMyProc;
param: integer;
stg : TStorage;
begin
b := procedure (var param: integer)
begin
param := 2*param;
end;
// stg.FDelegate := TMyProc(b); // doesn't compile in Delphi 2009, compiles in Delphi 2010
TMyProc(stg.FDelegate) := b;
param := 21;
TMyProc(stg.FDelegate)(param);
Writeln(param);
Readln;
end.
However, this doesn't work if casting to a local variable.
var
p: TProc;
a: TMyProc;
TMyProc(p) := a; // this will not compile
Curiouser and curiouser.
I have found a hack #2:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyProc = reference to procedure(param: integer);
var
a: TProc;
b: TMyProc;
begin
b := procedure (param: integer)
begin
Writeln('asdf');
end;
PPointer(@a)^ := PPointer(@b)^;
a;
readln;
end.
I am in doubt what are you trying to achieve by assigning TMyProc (with param argument) to TProc (without argument)?
Updated: A hack #3 (should increment ref counter, the idea is stolen from System._IntfCopy):
procedure AnonCopy(var Dest; const Source);
var
P: Pointer;
begin
P:= Pointer(Dest);
if Pointer(Source) <> nil
then IInterface(Source)._AddRef;
Pointer(Dest):= Pointer(Source);
if P <> nil then
IInterface(P)._Release;
end;
var
a: TProc;
b: TMyProc;
begin
b := procedure (param: integer)
begin
Writeln('asdf');
end;
AnonCopy(a, b);
// PPointer(@a)^ := PPointer(@b)^;
a;
readln;
end.
It appears that the best way would be to use generics to store the correct type of delegate in the record. No hacks required.
program Project51;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TMyProc = reference to procedure(var param: integer);
TStorage<T> = record
FDelegate: T;
end;
var
a : TMyProc;
b : TMyProc;
p : TProc;
param: integer;
stg : TStorage<TMyProc>;
begin
b := procedure (var param: integer)
begin
param := 2*param;
end;
stg.FDelegate := b;
param := 21;
stg.FDelegate(param);
Writeln(param);
Readln;
end.
精彩评论