代码之家  ›  专栏  ›  技术社区  ›  Johan

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

  •  3
  • Johan  · 技术社区  · 10 年前

    我有以下构造:

    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.
    

    显然,函数类型的记录需要在前向声明中声明。
    I know that I cannot declare the record as forward 但是

    是否有方法将过程变量声明为forward?
    或者我可以用一个旧的学校对象替换记录并声明 那个 作为前锋?

    3 回复  |  直到 7 年前
        1
  •  3
  •   David Heffernan    10 年前

    不能转发声明过程类型或记录。因此,结论是必须将类型定义放在记录中:

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

    FWIW,一旦我开始在记录中定义类型,我就倾向于用可见性说明符来分解声明。我会这样声明这种类型:

    type
      disassembler_info = record
      public
        type
          TPrint_address_func = function(info: disassembler_info): boolean;
      public
        data: string;
        print_address_func: TPrint_address_func;
      end;
    
        2
  •  3
  •   Rob Kennedy dkackman    10 年前

    如果你通过记录 指针,指针 ,那么这个问题很容易解决,即使在不支持嵌套记录类型的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. 编译器会自动用所有正确的值填充函数指针。它将它们存储在类的VMT中。您不可能因为意外忘记赋值而拥有空函数指针 print_address_func 。如果试图实例化类而不重写抽象方法,编译器将发出警告。

    2. 调用函数时不可能意外传递错误的记录指针。在您的设计中,调用函数如下所示:

      info.print_address_func(offset, info);
      

      如果您传递的记录参数与您调用其函数的记录不同,那么这肯定是一个错误。有了对象,冗余和出错的机会就消失了:

      info.print_address(offset);
      
    3. 无论您有多少个函数,类的单个实例的大小都保持不变,因为所有实例都共享一个VMT。在当前模型中,如果您有100个记录实例,那么将有100个相同函数指针的副本。

        3
  •  2
  •   LU RD    10 年前

    可以使用记录助手解决此问题。

    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 .