可以在FreePascal中的变体记录的情况下复制标识符吗?

这是我的问题:我想创建一个记录类型,其中一个变体记录的情况下,一些但不是全部将有一个特定的字段。 根据维基,这是完全合法的。 然而,当我试图编译下面的代码时:

program example;

{$mode objfpc}{$H+}

uses sysutils;

type
  maritalStates = (single, married, widowed, divorced);

  TPerson = record
    name: record
      first, middle, last: string;
    end;
    sex: (male, female);
    dob: TDateTime;
    case maritalStatus: maritalStates of
      single: ( );
      married, widowed: (marriageDate: TDateTime);
      divorced: (marriageDate, divorceDate: TDateTime;
        isFirstDivorce: boolean)      
  end;

var
  ExPerson: TPerson;

begin
ExPerson.name.first := 'John';
ExPerson.name.middle := 'Bob';
ExPerson.name.last := 'Smith';
ExPerson.sex := male;
ExPerson.dob := StrToDate('05/05/1990');
ExPerson.maritalStatus := married;
ExPerson.marriageDate := StrToDate('04/01/2015');

end.

编译失败,出现以下错误:

$ fpc ex.pas
Free Pascal Compiler version 3.0.0 [2016/02/14] for x86_64
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ex.pas
ex.pas(19,18) Error: Duplicate identifier "marriageDate"
ex.pas(21,3) Error: Duplicate identifier "marriageDate"
ex.pas(35,4) Fatal: There were 2 errors compiling module, stopping
Fatal: Compilation aborted
Error: C:lazarusfpc3.0.0binx86_64-win64ppcx64.exe returned an error exitcode

维基是错误的,还是我在这里错过了什么? 有什么办法可以达到我想要的效果吗?


非常有趣的问题。 我确信这是可能的。 如果您将您的代码修改为:

..
married, widowed, divorced: (marriageDate: TDateTime);
divorced: (divorceDate: TDateTime; isFirstDivorce: boolean)
..

它有效,但它不是你想要的结果。 由于婚姻日期和离婚日期相互重叠(如评论中所述!)

在这里输入图像描述

这张照片取自“Pascal用户手册(第4版)”,您可以看到变体部件具有相同的内存位置。

根据Pascal用户手册(第4版)和“Turbo Pascal ISBN 3-89011-060-6”这本书所描述的引用维基上的记录声明是无效的

  • 所有字段名称必须是不同的 - 即使它们出现在不同的变体中。
  • 如果变体是空的(即没有字段),则表单是:C :()
  • 字段列表只能有一个变体部分,并且必须遵循记录的固定部分。
  • 变体本身可以包含变体部分; 因此变体部分可以嵌套。
  • 在记录类型中引入的枚举类型常量标识符的作用域覆盖了封闭块。
  • 第1点是相关的一个! 建议的解决方案是“Turbo Pascal”,对于多次出现的字段名称使用唯一的前缀。

    在你的情况下你可能会看起来像:

    TPerson = record
        name: record
          first, middle, last: string;
        end;
        sex: (male, female);
        dob: TDateTime;
        case maritalStatus: maritalStates of
          single: ( );
          married, widowed: (marMarriageDate: TDateTime);
          divorced: (divMarriageDate, divorceDate: TDateTime;
            isFirstDivorce: boolean)      
      end;
    

    另一种解决方案是将已婚,已婚的......定义为一种记录类型。

    ..
    married       : (m: TMarried);
    divorced      : (d: TDivorced);
    ..
    

    这似乎工作

    program example;
    
    {$mode objfpc}{$H+}
    
    uses sysutils;
    
    type
      TMarried          = record
                            marriageDate  : TDateTime
                          end;
    
      TDivorced         = record
                            marriageDate  : TDateTime;
                            divorceDate   : TDateTime;
                            isFirstDivorce: boolean
                          end;
    
      TWidowed          = TMarried;
    
      maritalStates = (single, married, widowed, divorced);
    
      TPerson = record
        name: record
          first, middle, last: string;
        end;
        sex: (male, female);
        dob: TDateTime;
        case maritalStatus: maritalStates of
          single        : ();
          married       : (m: TMarried);
          widowed       : (w: TWidowed);
          divorced      : (d: TDivorced);
      end;
    
    var ExPerson: TPerson;
    
    begin
      with ExPerson do
      begin
        name.first := 'John';
        name.middle := 'Bob';
        name.last := 'Smith';
        sex := male;
        dob := StrToDate('05/05/1990');
        maritalStatus := married;
        m.marriageDate := StrToDate('04/01/2015');
      end;
    end.
    

    编辑:你也可以定义内联的记录,但我认为上面更清晰。 以下是另一种方式:

    program example;
    
    {$mode objfpc}{$H+}
    
    uses sysutils;
    
    type
      maritalStates = (single, married, widowed, divorced);
    
      TPerson = record
        name: record
          first, middle, last: string;
        end;
        sex: (male, female);
        dob: TDateTime;
        case maritalStatus: maritalStates of
          single   : ();
          married  : (m: record marriageDate: TDateTime end);
          widowed  : (w: record marriageDate: TDateTime end);
          divorced : (d: record
                           marriageDate  : TDateTime;
                           divorceDate   : TDateTime;
                           isFirstDivorce: boolean
                         end)
      end;
    
    var ExPerson: TPerson;
    
    begin
      with ExPerson do
      begin
        name.first  := 'John';
        name.middle := 'Bob';
        name.last   := 'Smith';
        sex := male;
        dob := StrToDate('05/05/1990');
        maritalStatus  := married;
        m.marriageDate := StrToDate('04/01/2015');
      end;
    end.
    

    Baltasar提出的建议将会编译,但不会做你想要的。 marriageDatedivorceDate将重叠,并且写入其中一个也会修改另一个,因为他们只是在相同的地址。

    但在这种情况下,根本没有充分的理由为变体记录。

    为什么不简单:

    type
      maritalStates = (single, married, widowed, divorced);
    
      TPerson = record
        name: record
          first, 
          middle, 
          last: string;
        end;
        sex: (male, female);
        dob: TDateTime;
        maritalStatus: maritalStates; // single, married, widowed, divorced
        marriageDate: TDateTime;      // married, widowed, divorced   
        divorceDate   : TDateTime;    // divorced
        isFirstDivorce: boolean;      // divorced
      end;
    

    使用和布局正是您所需要的。 如果一个字段不适用(例如marriageDatesingle ,或divorceDatemarried ),则不要使用它。

    这与变体记录相同。 在那里你也只设置适用的字段。 请注意,编译器或运行时不会阻止您写入变体记录的错误字段,即在变体记录中,如果状态为single ,您仍然可以写入或从divorceDate读取,即使这没有任何意义。

    如果你想区分几种不同的设置,只需在评论中做到这一点,并忘记变体记录,你不需要在这里。 现在你可以这样做:

    var
      P: TPerson;
    begin
      P.name.first := 'Bob';
      P.name.middle := 'The';
      P.name.last := 'Builder';
      P.sex := male;
      P.dob := StrToDate('05/05/1980');
      P.maritalStatus := divorced;
      P.marriageDate := StrToDate('04/01/2013');
      P.divorceDate := StrToDate('04/02/2016');
      P.isFirstDivorce := True;
    
      // etc...
    

    更新

    只是为了表明绝对不需要制作这个记录变体,

    我将发布我的Project62.dpr,它显示了相应字段和相同记录大小的完全相同的偏移量:

    program Project62;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils;
    
    type
      maritalStates = (single, married, widowed, divorced);
      tsex = (male, female);
    
      // No variant part
      PPerson = ^TPerson;
      TPerson = record
        name: record
          first,
          middle,
          last: string;
        end;
        sex: tsex;
        dob: TDateTime;
        maritalStatus: maritalStates; // single, married, widowed, divorced
        marriageDate: TDateTime;      // married, widowed, divorced
        divorceDate   : TDateTime;    // divorced
        isFirstDivorceDate: boolean;      // divorced
      end;
    
      // Variant part like tonypdmtr's record
      PPerson2 = ^TPerson2;
      TPerson2 = record
        name: record
          first,
          middle,
          last: string;
        end;
        sex: tsex;
        dob: TDateTime;
        case maritalStatus: maritalStates of
          single:   ();
          widowed:  (w: record marriageDate: TDateTime; end); // overlaps with m.marriageDate and d.marriageDate
          married:  (m: record marriageDate: TDateTime; end); // overlaps with w.marriageDate and d.marriageDate
          divorced: (d: record
                          marriageDate: TDateTime;            // overlaps with w.marriageDate and m.marriageDate
                          divorceDate: TDateTime;             // same offset as in my non-variant version
                          isFirstDivorceDate: Boolean         // same offset as in my non-variant version
                        end);
      end;
    
    begin
      try
        Writeln('TPerson:  size = ', Sizeof(TPerson));
        Writeln('TPerson.maritalStatus:         offset = ', NativeUInt(@PPerson(nil)^.maritalStatus));
        Writeln('TPerson.marriageDate:          offset = ', NativeUInt(@PPerson(nil)^.marriageDate));
        Writeln('TPerson.divorceDate:           offset = ', NativeUInt(@PPerson(nil)^.divorceDate));
        Writeln('TPerson.isFirstDivorceDate:    offset = ', NativeUInt(@PPerson(nil)^.isFirstDivorceDate));
        Writeln;
        Writeln('TPerson2:  size = ', Sizeof(TPerson2));
        Writeln('TPerson2.maritalStatus:        offset = ', NativeUInt(@PPerson2(nil)^.maritalStatus));
        Writeln('TPerson2.w.marriageDate:       offset = ', NativeUInt(@PPerson2(nil)^.w.marriageDate));
        Writeln('TPerson2.m.marriageDate:       offset = ', NativeUInt(@PPerson2(nil)^.m.marriageDate));
        Writeln('TPerson2.d.marriageDate:       offset = ', NativeUInt(@PPerson2(nil)^.d.marriageDate));
        Writeln('TPerson2.d.divorceDate:        offset = ', NativeUInt(@PPerson2(nil)^.d.divorceDate));
        Writeln('TPerson2.d.isFirstDivorceDate: offset = ', NativeUInt(@PPerson2(nil)^.d.isFirstDivorceDate));
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      Readln;
    end.
    

    输出(在Windows上):

    TPerson:  size = 56
    TPerson.maritalStatus:         offset = 24
    TPerson.marriageDate:          offset = 32
    TPerson.divorceDate:           offset = 40
    TPerson.isFirstDivorceDate:    offset = 48
    
    TPerson2:  size = 56
    TPerson2.maritalStatus:        offset = 24
    TPerson2.w.marriageDate:       offset = 32
    TPerson2.m.marriageDate:       offset = 32
    TPerson2.d.marriageDate:       offset = 32
    TPerson2.d.divorceDate:        offset = 40
    TPerson2.d.isFirstDivorceDate: offset = 48
    

    32位的布局可以放在一个简单的图中,如下所示:

      00 TPerson: [name.first]          TPerson2: [name.first]
      04          [name.middle]                   [name.middle]
      08          [name.last]                     [name.last]
      12          [sex]                           [sex]
      16          [dob]                           [dob]
      24          [maritalStatus]                 [maritalStatus]
      32          [marriageDate]                  [w.marriageDate] [m.marriageDate] [d.marriageDate]
      40          [divorceDate]                                                     [d.divorceDate]
      48          [isFirstDivorceDate]                                              [d.isFirstDivorceDate]
    
    链接地址: http://www.djcxy.com/p/91403.html

    上一篇: Can Identifiers be duplicated across cases of a variant record in FreePascal?

    下一篇: Why does repeated memory allocation in windows slow down?