使用存储管理器分配内存会导致非纯函数 (RM 13.1(22))

Den*_*e P 1 allocation ada gnat

将一些旧代码移植到较新的 CentOs Linux 机器。我使用带有几个标志的 linux gnat:

Default_Switches ("ada") use ("-fstack-check", "-g", "-gnatVr", "-gnato", "-gnatE", "-gnatwmuv", "-gnata", "-m32");
Run Code Online (Sandbox Code Playgroud)

我有 gnat 版本:

gcc-gnat.i686   4.8.5-11.el7
Run Code Online (Sandbox Code Playgroud)

所以这些是前提条件。我现在有一个肯定可以工作的自写存储管理器,它被称为

St_Wa.Alloc(StoragePool, BitSize)
Run Code Online (Sandbox Code Playgroud)

所以现在我的问题是,说实话,我真的不明白编译器失败的原因,所以我将非常感谢您详细解释为什么它不起作用!

function AllocMem(StoragePool : in St_Wa.Mem_Pool_Type;
                  Option: in Option_Type) 
                  return Option_Ref is

 subtype New_Type is Option_Type (Option.Kind);

 New_Option : New_Type;

for New_Option use at St_Wa.Alloc( StoragePool => StoragePool, 
                                    BitSize => New_Type'Size)
begin 
    Bl_Bl.Move( ... sth happens here ... )
    return Pointer(New_Option'Address);
end AllocMem;
Run Code Online (Sandbox Code Playgroud)

然而:

type Option_Type ( Kind : Option_Kind_Type := Marker) is
record
  Next : Option_Ref;
  case Kind is
    when First_Procedure => First_Procedure : First_Procedure_Type;
    when Sec_Procedure => Sec_Procedure : Sec_Procedure_Type;
  end case;
end record;
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

invalid address clause for initialized object "New_Option"
function "Alloc" is not pure (RM 13.1 (22))
Run Code Online (Sandbox Code Playgroud)

我是否会收到此错误,因为我有一个带有条件的类型的开关盒,因此尺寸仅根据种类确定?在不重写所有内容的情况下如何避免这种情况?

fly*_*lyx 5

我是否会收到此错误,因为我有一个带有条件的类型的开关盒,因此尺寸仅根据种类确定?

不可以。LRM 中的错误消息 (RM 13.1 (22)) 引用的段落内容如下:

实现不需要支持包含非静态表达式的表示项,除非如果表示项中的每个非静态表达式是静态表示在实体之前声明的常量的名称,那么实现应该支持给定实体的表示项。

Alloc现在,这里的表示项是从您的代码开始的调用:

for New_Option use at St_Wa.Alloc(StoragePool => StoragePool, 
                                  BitSize => New_Type'Size);
Run Code Online (Sandbox Code Playgroud)

是 Ada83 风格的

for New_Option'Address use St_Wa.Alloc(StoragePool => StoragePool, 
                                       BitSize => New_Type'Size);
Run Code Online (Sandbox Code Playgroud)

由于Alloc (...)是函数调用,因此它不是静态表达式,因为根据 RM 4.9,静态函数是:

  • 预定义运算符,其参数和结果类型均为标量类型,其中没有一个是形式标量类型的后代;
  • 预定义的连接运算符,其结果类型为字符串类型;
  • 枚举文字;
  • 如果前缀表示静态标量子类型,并且参数和结果类型是标量,则为函数的语言定义属性。

由于Alloc以上都不是,如 RM 13.1 所示,实现不需要在表示项中支持它。然而,实际的错误消息告诉我们“Alloc”不是 pure,因此 GNAT 告诉它如果是 pure 它将支持这一点Alloc。

因此解决这个问题的一种方法是使其成为Alloc纯的,这意味着:添加pragma Pure;到包中St_Wa,其中包含Alloc. 这是否可行取决于软件包,并且可能需要额外的更改。

如果这不可行,RM 13.1 (22) 暗示了另一种方式:如果非静态表达式表示在实体之前声明的常量,则应该支持它。因此,这应该有效:

My_Address : constant System.Address :=
  St_Wa.Alloc(StoragePool => StoragePool, BitSize => New_Type'Size);

New_Option : New_Type;

for New_Option use at My_Address;
Run Code Online (Sandbox Code Playgroud)