相互依赖的过程变量和记录

3
我有以下结构:
program Project26;

{$APPTYPE CONSOLE}
{$R *.res}
type

  TPrint_address_func = function(offset: integer; info: disassembler_info): boolean;

  disassembler_info = record
    data: string;
    print_address_func: TPrint_address_func;
  end;

begin
end.

很明显,函数类型的记录需要在前向声明中声明。
我知道我不能将记录声明为 forward,但是... 有没有一种方法可以将过程变量声明为 forward?
或者我可以用老式的对象来替换记录并将其声明为 forward 吗?
3个回答

3

你无法对过程类型或记录进行前置声明。因此,结论是你必须将类型定义放在记录内部:

type
  disassembler_info = record
  type
    TPrint_address_func = function(info: disassembler_info): boolean;
  var
    data: string;
    print_address_func: TPrint_address_func;
  end;

顺带一提,一旦我开始在记录内定义类型,我倾向于使用可见性修饰符来分割声明。我会像这样声明此类型:

type
  disassembler_info = record
  public
    type
      TPrint_address_func = function(info: disassembler_info): boolean;
  public
    data: string;
    print_address_func: TPrint_address_func;
  end;

好的,这意味着类型定义仍将在记录外可见,对吗? - Johan
1
@Johan 是的,它是可见的,但它的名称是 disassembler_info.TPrint_address_func。这是你能做到的最好的方式。 - David Heffernan
@Johan,你可以查看一下,但我预测答案是否定的。由于Delphi编译器的架构,object是一个值类型,因此你只能前向声明引用类型。 - David Heffernan
3
你不能声明一个类型别名,TPrint_address_func_alias = disassembler_info.TPrint_address_func;吗? - LU RD
@LURD 哦,你肯定可以这样做。我的意思是你不能避免在记录内部声明类型。是的,你可以在外部声明一个别名。 - David Heffernan
显示剩余2条评论

3
如果您传递一个记录指针,即使在不支持嵌套记录类型的Delphi版本中,这个问题也很容易解决。先前声明一个记录指针类型,然后使用记录指针声明函数类型。最后,声明该记录:
type
  PDisassembler_info = ^TDisassembler_info;
  TPrint_address_func = function(offset: Integer;
                                 info: PDisassembler_info): Boolean;
  TDisassembler_info = record
    data: string;
    print_address_func: TPrint_address_func;
  end;

您可能会有不止一个函数指针,也可能会有不止一个记录实例。随着您扩展这个模式,最终会重新发明 。请考虑以下内容:

type
  TDisassembler_info = class
    data: string;
    function print_address(offset: Integer): Boolean; virtual; abstract;
  end;

现在,您不再需要定义一个自由函数,而是声明该类的后代并覆盖抽象方法。随着函数指针和记录实例数量的增长,这样做有几个优点:

  1. The compiler automatically fills in the function pointers with all the right values. It stores them in the class's VMT. There's no chance you'll have a null function pointer by accidentally forgetting to assign print_address_func. The compiler will warn if you attempt to instantiate a class without overriding the abstract methods.

  2. It's impossible to accidentally pass the wrong record pointer when you call the function. In your design, calling the function will look like this:

    info.print_address_func(offset, info);
    

    It would surely be an error if the record parameter you passed differed from the record whose function you called. With an object, the redundancy and opportunity for error go away:

    info.print_address(offset);
    
  3. No matter how many functions you have, the size of a single instance of the class remains constant because all instances share a single VMT. In your current model, if you have 100 instances of your record, you'll have 100 copies of the same function pointer.


谢谢Rob,但我特别不想要一个类。你提出了一个有效的观点。至于指针,它会让代码库混乱不堪。 - Johan

2

可以使用记录助手来解决这个问题。

Type
  disassembler_info = record
    private
      FP: Pointer;
    public
      data: string;
  end;

  TPrint_address_func = function(info: disassembler_info): boolean;

  disassembler_info_helper = record helper for disassembler_info
  private
     procedure SetAFunc(aF: TPrint_Address_Func);
     function GetAFunc: TPrint_Address_Func;
  public
    property print_address_func: TPrint_address_func read GetAFunc write SetAFunc;
  end;

function disassembler_info_helper.GetAFunc: TPrint_Address_Func;
begin
  Result := TPrint_address_func(FP);
end;

procedure disassembler_info_helper.SetAFunc(aF: TPrint_Address_Func);
begin
  TPrint_address_func(FP) := TPrint_address_func(aF);
end;

function MyFunc(aRec: disassembler_info): boolean;
begin
  Result := true;
  WriteLn('Hello from MyFunc');
end;

var
  aFunc: TPrint_address_func;
  aRec:disassembler_info;

begin
  aRec.print_address_func := MyFunc;
  aFunc := arec.print_address_func;
  if aFunc(aRec) then begin
    WriteLn('Voila!');
  end;
  ReadLn;
end. 

助手会翻译如下内容:

助手会注入一个具有读写方法的TPrint_address_func属性,该属性操作在disassembler_info中声明的私有变量。


2
嗯,我很钦佩你的机智。;-) - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接