首页 > 解决方案 > 使用不能被 8 整除的模块化类型时引发 Constraint_Error

问题描述

我遇到了一个问题,即在 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;

这是我用来编译这个例子的项目文件:

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;

我似乎在 RM 中找不到任何可以说明为什么会发生这种情况的东西。

编辑:下面的西蒙赖特已经弄清楚为什么会发生这种情况。我幼稚的理解是,Buffer_Type_Two在指定的内存地址上覆盖的一个实例会将这个位置的内存解释为 12 位值的序列。看来情况并非如此。似乎编译器将类型的大小四舍五入到 16 位,然后Constraint_Error在从数组读取的 16 位值不符合 12 位类型时引发 a。

如果有人能想到一种更好的方法来以顺序方式从内存中的某个位置读取一系列 12 位值,我将不胜感激,谢谢。

标签: adagnat

解决方案


使用最近的 GNAT,您可以通过定义Buffer_Type_Two

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Pack;

ARM 13.2(9)警告说,对于 13 位值,这可能无法满足您的要求(不过,最近的 GNAT 会这样做)。

另一种选择是

type Buffer_Type_Two is array (1 .. 128) of Type_Two
  with Component_Size => 12;

结果是

...
Testing type two
Test:  32
Test:  514
Test:  32
Test:  514
...

对于 13 位,无论采用哪种方法,

...
Testing type two
Test:  32
Test:  257
Test:  2056
Test:  64
Test:  514
Test:  4112
Test:  128
Test:  1028
Test:  32
...

但是,对于嵌入式目标,您需要使用-full-运行时系统;对于其他人,正如上面@egilhh 所指出的,

ajxs.adb:14:09: packing of 12-bit components not supported by configuration

推荐阅读