Ada:组件与父级重叠的派生记录

Ada:组件与父级重叠的派生记录,ada,Ada,我试图为两个密切相关的不同设备定义一个硬件接口,但其中一个设备的功能略多于另一个 作为问题的一个非常简化的版本,让我们假设: 设备A有2个寄存器:R0未使用,R1已使用 设备B有2个寄存器:同时使用R0和R1 我试图对代码和定义产生兴趣,并使用多态定义重复使用,如: Tag_Length : constant := Standard'Address_Size / System.Storage_Unit; type A_IO is tagged record --

我试图为两个密切相关的不同设备定义一个硬件接口,但其中一个设备的功能略多于另一个

作为问题的一个非常简化的版本,让我们假设:

  • 设备A有2个寄存器:R0未使用,R1已使用
  • 设备B有2个寄存器:同时使用R0和R1
我试图对代码和定义产生兴趣,并使用多态定义重复使用,如:

   Tag_Length : constant := Standard'Address_Size / System.Storage_Unit;

   type A_IO is tagged record
      --  RO precedes, is unused but the space is reserved
      R1 : Byte;
   end record;
   for A_IO use record
      R1 at Tag_Length + 1 range 0 .. 7;
   end record;
   
   type B_IO is new A_IO with record
      R0 : Byte;
      --  R1 would follow as defined by the parent record
   end record;
   for B_IO use record
      R0 at Tag_Length + 0 range 0 .. 7;
   end record;
这会导致编译器错误,这在大多数情况下都很有意义:
组件与“B_IO”
的父字段重叠(GNAT Community 2019)

除此之外,我还有其他选择,包括:

  • 对每个设备使用相同的类型(con:设备A将看到不应该可见的组件)
  • 使用完全不同的类型,在使用共享代码时,依靠通过访问类型进行的未经检查的转换来更改对象的视图(con:将涉及多次重新定义某些相同的组件)

我想知道是否有一种可行的方法没有任何上述缺点。

我不知道这是否是一个bug。在7.1和7.2系列中,它可以很好地编译,但在8.2和9.1中,它无法编译

由于您愿意使用带标记的记录,并让标记在位布局中占据空间,因此一个潜在的解决方法是使用变量未标记的记录并用变量替换标记。考虑:

类型记录\u选择为(A\u IO,B\u IO);
类型Shared_Record(S:Record_Select)为Record
R1:字节;
案例S是
当A_IO=>null时;
当B_IO=>R0时:字节;
终例;
结束记录;
对于共享记录,请使用记录
S在0范围0。。15;
R0在2范围0。。7.
R1在3范围内0。。7.
结束记录;
对于共享_记录的大小,使用32;
您可以调整大小以匹配实际标记大小。我只是加入了一些价值观。这将为您提供与标记记录相似的布局(当然减去大小差异)

此外,如果将变量参数设置为具有默认值,则可以在两个变量之间进行复制,而无需未经检查的转换,只要在类型中定义它们而不使用变量约束:

类型记录\u选择为(A\u IO,B\u IO);
--请注意S的默认值
类型共享_记录(S:Record\u Select:=B_IO)为记录
R1:字节;
案例S是
当A_IO=>null时;
当B_IO=>R0时:字节;
终例;
结束记录;
对于共享记录,请使用记录
S在0范围0。。15;
R0在2范围0。。7.
R1在3范围内0。。7.
结束记录;
对于共享_记录的大小,使用32;
--请注意无约束类型定义
A:共享的IO记录:=(S=>A IO,R1=>3);
B:共享记录:=(S=>bio,R0=>1,R1=>2);
开始
放线(B.R1’图像);
B:=A;
放线(B.R1’图像);
输出:

 2
 3

以下是一些从中获得灵感并在此基础上发展起来的方法:

使用访问类型限制视图

  • 与Jere的一样,它使用了一个记录,该记录将与依赖于变量来定义重叠字段的两个实现共享
  • 使用Unchecked_Union特性来消除存储变量的需要
  • 将硬件IO定义封装在标记记录中,允许在两个设备实现之间进行继承和OO。无论设备的软件实现是为了保持内部状态,而不仅仅是IO结构,都需要某种封装
  • 使用访问类型确保每个设备实现只能访问正确的组件(无需依赖未检查的转换)
封装设备是
类型记录_选择为(A_IO,B_IO);
类型共享IO(S:Record\u Select)为Record
R1:字节;
案例S是
当A_IO=>null时;
当B_IO=>R0时:字节;
终例;
结束未选中的联合的记录;
对于共享IO使用记录
R0在0范围0。。7.
R1在1范围0。。7.
结束记录;
类型Root是抽象标记的private;
类型IO_Access是访问所有共享IO;
函数Get_IO_Access(R:in-out Root)返回IO_Access;
私有的
类型Root是抽象标记的记录
IO:别名共享IO(B_IO);--可以是A_IO/B_IO
结束记录;
终端设备;
A包是
类型Device是新设备。根目录为private;
程序测试(Dev:输入输出设备);
私有的
类型A_IO_Access是访问所有设备。共享_IO(设备。A_IO);
类型设备是新设备。根目录下有记录
IO:A_IO_访问;
结束记录;
结束A;
B包是
类型设备是新的A.具有专用功能的设备;
最重要的
程序测试(Dev:输入输出设备);
私有的
类型B_IO_Access是访问所有设备。共享_IO(设备。B_IO);
类型设备是新的A.具有记录的设备
IO:B_IO_访问;
结束记录;
B端;
包体设备是
函数Get\u IO\u Access(R:in-out Root)返回IO\u访问权限为
开始
返回R.IO'Unchecked_访问;
结束获取IO访问;
终端设备;
包体A是
程序测试(Dev:in-out设备)为
开始
--此分配通常在对象初始化时完成
Dev.IO:=A_IO_访问(Get_IO_访问(Dev));
--能见度测试
Dev.IO.R0:=0;--触发编译器警告(很好!不确定为什么这不是编译时错误)
Dev.IO.R1:=1;--合法的
结束试验;
结束A;
包体B是
最重要的
程序测试(Dev)