我正在尝试为两个密切相关的不同设备定义硬件接口(interface),但一个设备的功能比另一个设备稍多。
作为问题的一个非常简化的版本,让我们说:
- 设备 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 社区 2019)。
我有替代方案,其中涉及:
- 为每个设备使用相同的类型(缺点:设备 A 将看到不应可见的组件)
- 使用完全不同的类型,在使用共享代码时通过访问类型依靠未经检查的转换来更改对象的 View (缺点:将涉及多次重新定义某些相同的组件)
我想知道是否有一种可行的方法,没有任何提到的缺点。
最佳答案
我不知道这是否是一个错误。在7.1和7.2系列中它编译得很好,但在8.2和9.1中它无法编译。
由于您愿意使用标记记录并让标记占用位布局中的空间,因此潜在的解决方法是使用变体未标记记录并用变体替换标记。考虑:
type Record_Select is (A_IO, B_IO);
type Shared_Record(S : Record_Select) is record
R1 : Byte;
case S is
when A_IO => null;
when B_IO => R0 : Byte;
end case;
end record;
for Shared_Record use record
S at 0 range 0 .. 15;
R0 at 2 range 0 .. 7;
R1 at 3 range 0 .. 7;
end record;
for Shared_Record'Size use 32;
您可以调整尺寸以匹配您的实际标签尺寸。我只是添加了一些值。这将为您提供与标记记录类似的布局(当然减去大小差异)。
此外,如果您将变体参数设置为具有默认值,则可以在两个变体之间进行复制,而无需未经检查的转换,只要您在类型中没有变体约束的情况下定义它们即可:
type Record_Select is (A_IO, B_IO);
-- Note the default value for S
type Shared_Record(S : Record_Select := B_IO) is record
R1 : Byte;
case S is
when A_IO => null;
when B_IO => R0 : Byte;
end case;
end record;
for Shared_Record use record
S at 0 range 0 .. 15;
R0 at 2 range 0 .. 7;
R1 at 3 range 0 .. 7;
end record;
for Shared_Record'Size use 32;
-- Note the unconstrained type definitions
A : Shared_Record := (S => A_IO, R1 => 3);
B : Shared_Record := (S => B_IO, R0 => 1, R1 => 2);
begin
Put_Line(B.R1'Image);
B := A;
Put_Line(B.R1'Image);
输出:
2
3
关于Ada:组件与父级重叠的派生记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64001010/