Кастинг анонимных процедур в Delphi 2009 - PullRequest
5 голосов
/ 21 февраля 2010

Следующий код (созданный только для демонстрации проблемы) компилируется и работает в Delphi 2010. В Delphi 2009 сбои компилятора с «E2035 Недостаточно реальных параметров».

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.

Я нашел только один очень уродливый хак, чтобы обойти эту проблему (a: TProc absolute b). Кто-нибудь знает более хороший обходной путь для этого недостатка компилятора?

[Поле TProc фактически скрыто внутри записи, в которой может храниться различный «исполняемый» код - TProcedure, TMethod и TProc. Кастинг используется для хранения определенного анонимного процесса в этом поле.]

Ответы [ 3 ]

2 голосов
/ 22 февраля 2010

Хитрость не в том, чтобы делать

a := TProc(b);

но

TMyProc(a) := b;

Это компилируется и работает в D2009. Пример проекта прилагается ниже.

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.

Однако, это не работает, если приведение к локальной переменной.

var
  p: TProc;
  a: TMyProc;

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

Любопытнее и страннее.

1 голос
/ 22 февраля 2010

Похоже, что наилучшим способом было бы использование обобщений для хранения в записи правильного типа делегата. Нет необходимости в взломах.

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.
1 голос
/ 22 февраля 2010

Я нашел взлом # 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.

Я сомневаюсь, чего вы пытаетесь достичь, назначая TMyProc (с аргументом param) для TProc (без аргумента)?


Обновлено : хак № 3 (если увеличить счетчик ссылок, идея украдена из 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.
...