2010-02-21 9 views
5

Der folgende Code (nur zur Veranschaulichung des Problems erstellt) wird in Delphi 2010 kompiliert und funktioniert. In Delphi 2009 schlägt der Compiler mit "E2035 Nicht genug tatsächliche Parameter" fehl. (: TPROC absolute b a)Casting anonymer Prozeduren in Delphi 2009

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. 

Ich habe nur eine sehr hässliche Hack zu arbeiten, um das Problem zu finden. Kennt jemand eine bessere Problemumgehung für diesen Compiler-Mangel?

[TProc-Feld ist tatsächlich in einem Datensatz versteckt, der verschiedene 'ausführbare' Code speichern kann - TProcedure, TMethod und TProc. Gießen verwendet wird, bestimmte anonyme proc in dieses Feld zu speichern]

Antwort

1

Ich habe einen Hack # gefunden. 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. 

Ich bin im Zweifel, was Sie durch die Zuordnung TMyProc (mit param Argumente) zu erreichen versuchen zu TProc (ohne Argument)?


Aktualisiert: Ein Hack # 3 (sollte ref Zähler erhöhen, wird die Idee von System._IntfCopy gestohlen):

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. 
+0

Es funktioniert im Testfall, aber nicht in meinem (etwas komplizierteren) Fall. Da läuft etwas schief mit der Schnittstellenreferenz. Ich werde versuchen, einen genaueren Testfall zusammenzustellen. TProc ist nur ein Speicherbereich für verschiedene 'Verweis auf Prozedur' Berechtigungen, deshalb bin ich Casting es. Vielleicht könnte etwas schöneres mit den Generika gemacht werden ... – gabr

+0

@gabr: Ich habe meinen Beitrag aktualisiert, um Hack # 3 vorzuschlagen (sollte Interface ref counter inkrementieren) – kludg

+0

Danke für all deine Arbeit, aber ich habe gerade einen wirklich einfachen Weg gefunden löse dieses Problem ... – gabr

2

Der Trick nicht zu tun, ist

a := TProc(b); 

aber

TMyProc(a) := b; 

Das kompiliert und arbeitet in D2009. Beispielprojekt unten angehängt

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. 

Dies funktioniert jedoch nicht, wenn eine lokale Variable umgewandelt wird.

var 
    p: TProc; 
    a: TMyProc; 

TMyProc(p) := a; // this will not compile 

Neugieriger und neugieriger.

1

Es scheint, dass der beste Weg wäre, Generika zu verwenden, um den richtigen Delegattyp im Datensatz zu speichern. Keine Hacks erforderlich.

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.