我遇到了一个问题,在 Ada 中使用不能被系统Storage_Unit(如运行时定义的system.ads)整除的模块化类型将Constraint_Error在运行时在访问时引发 a 。我最初在使用最小运行时间的裸机系统上遇到这个问题,同时尝试通过将 12 位数组覆盖在内存中的缓冲区上来从缓冲区读取 12 位值。有谁知道为什么会这样?
以下最小示例说明了我遇到的问题。我使用 AdaCore 的 GNAT 2019 对此进行了测试,并使用包含的zfp运行时编译。使用标准运行时不会重现该问题。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
----------------------------------------------------------------------------
-- Modular type with standard size divisible by 8.
----------------------------------------------------------------------------
type Type_One is mod 2 ** 16;
type Buffer_Type_One is array (1 .. 128) of Type_One;
----------------------------------------------------------------------------
-- Modular type with non-base 8 size.
----------------------------------------------------------------------------
type Type_Two is mod 2 ** 12;
type Buffer_Type_Two is array (1 .. 128) of Type_Two;
type Buffer is array (1 .. 256) of Character;
----------------------------------------------------------------------------
-- Example buffer.
----------------------------------------------------------------------------
Test_Buffer : Buffer := (others => ' ');
begin
----------------------------------------------------------------------------
-- Will not raise an exception.
----------------------------------------------------------------------------
Test_One :
declare
Buffer_One : Buffer_Type_One
with Import,
Convention => Ada,
Address => Test_Buffer'Address;
begin
Put_Line ("Testing type one");
for I in Natural range 1 .. 16 loop
Put_Line ("Test: " & Buffer_One (I)'Image);
end loop;
end Test_One;
----------------------------------------------------------------------------
-- Will raise a constraint error at runtime.
----------------------------------------------------------------------------
Test_Two :
declare
Buffer_Two : Buffer_Type_Two
with Import,
Convention => Ada,
Address => Test_Buffer'Address;
begin
Put_Line ("Testing type two");
for I in Natural range 1 .. 16 loop
Put_Line ("Test: " & Buffer_Two (I)'Image);
end loop;
exception
when Constraint_Error =>
Put_Line ("Constraint error encountered.");
end Test_Two;
end Main;
Run Code Online (Sandbox Code Playgroud)
这是我用来编译这个例子的项目文件:
project Test is
for Object_Dir use "obj";
for Exec_Dir use "build";
for Create_Missing_Dirs use "True";
for Languages use ("Ada");
package Builder is
for Executable ("main.adb") use "test";
end Builder;
for Main use ("main.adb");
package Compiler is
for Default_Switches ("Ada") use (
"-gnat2012",
"-gnatwadehl",
"-gnatVa",
"-gnaty3abcdefhiklmnoprstux"
);
end Compiler;
for Runtime ("Ada") use "zfp";
end Test;
Run Code Online (Sandbox Code Playgroud)
我似乎无法在 RM 中找到任何可以表明为什么会发生这种情况的内容。
编辑:下面的西蒙赖特已经弄清楚为什么会发生这种情况。我天真的理解是,Buffer_Type_Two在指定内存地址处重叠的实例会将这个位置的内存解释为 12 位值的序列。情况似乎并非如此。似乎编译器将类型的大小四舍五入到 16 位,然后Constraint_Error在从数组中读取的 16 位值不符合 12 位类型时引发 a 。
如果有人能想到一种更好的方法来以顺序方式从内存中的某个位置读取 12 位值序列,我将不胜感激,谢谢。
使用最近的 GNAT,您可以通过定义Buffer_Type_Two为实现您想要的行为
type Buffer_Type_Two is array (1 .. 128) of Type_Two
with Pack;
Run Code Online (Sandbox Code Playgroud)
ARM 13.2(9)警告说,对于 13 位值,这可能无法满足您的要求(不过,最近的 GNAT 可以做到)。
另一种选择是
type Buffer_Type_Two is array (1 .. 128) of Type_Two
with Component_Size => 12;
Run Code Online (Sandbox Code Playgroud)
结果是
...
Testing type two
Test: 32
Test: 514
Test: 32
Test: 514
...
Run Code Online (Sandbox Code Playgroud)
对于 13 位,无论采用哪种方法,
...
Testing type two
Test: 32
Test: 257
Test: 2056
Test: 64
Test: 514
Test: 4112
Test: 128
Test: 1028
Test: 32
...
Run Code Online (Sandbox Code Playgroud)
但是,对于嵌入式目标,您需要使用-full-运行时系统;对于其他人,正如上面@egilhh 所指出的,
ajxs.adb:14:09: packing of 12-bit components not supported by configuration
Run Code Online (Sandbox Code Playgroud)