在 Delphi 2009 中铸造匿名过程

发布于 2024-08-22 04:58:51 字数 578 浏览 5 评论 0原文

以下代码(仅为演示问题而构建)在 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.

我发现只有一种非常丑陋的 hack 可以解决这个问题(a:TProc 绝对 b)。有人知道针对这个编译器缺陷的更好的解决方法吗?

[TProc 字段实际上隐藏在可以存储各种“可执行”代码的记录内 - TProcedure、TMethod 和 TProc。转换用于将特定的匿名过程存储到该字段中。]

The following code (constructed only to demonstrate the problem) compiles and works 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.]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

葮薆情 2024-08-29 04:58:51

诀窍是不这样做

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

越来越好奇。

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.

把昨日还给我 2024-08-29 04:58:51

我发现了一个 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.

我怀疑你想通过将 TMyProc (带参数参数)分配给 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.

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.
猫九 2024-08-29 04:58:51

看来最好的方法是使用泛型在记录中存储正确的委托类型。无需黑客攻击。

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.

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文