首页 文章

如何将对约束字符串的访问权限传递给Ada中的子程序

提问于
浏览
3

好的 . 我真的很讨厌这样简单的问题,但我已经完全阅读了三本不同的书籍,向我解释了访问类型和参数模式,我无法理解我做错了什么 .

我在Ada中创建了一个简单的shell,因为我对此很感兴趣,并且我认为到目前为止这是一个很好的学习经历 . 这是我的代码:

with Ada.Text_IO;
with Execute_System;

procedure Main is
   package IO renames Ada.Text_IO;
   Input : aliased String(1 .. 255) := (others=> ' ');
   Last: Integer;

begin
   IO.Put_Line("Welcome to ash! This is an extreme work in progress.");
   Main_Loop:
   loop
      Input := (others=> ' ');
      IO.Put("ash> ");
      IO.Get_Line(Input, Last);
      if Input(Input'First..Last) = "quit" then
       exit Main_Loop;
      else
       Execute_System(Command => Input'Access);
      end if;
   end loop Main_Loop;
end Main;

Execute_System()所做的是传递给Spawn,而后者又由GNAT.OS_Lib库提供 . 编译时得到的错误是:

main.adb:6:04: warning: aliased object has explicit bounds
main.adb:6:04: warning: declare without bounds (and with explicit initialization)
main.adb:6:04: warning: for use with unconstrained access
main.adb:19:36: object subtype must statically match designated subtype
execute_system.adb:5:60: prefix of "Access" attribute must be aliased
gnatmake: "main.adb" compilation error

我不允许访问此字符串,因为它具有明确的边界 . 我已经在 new subtype Command_Access is access all String(1..255) 中看到了一个解决方案,但我还没有对子类型有所了解 .

有人可以解释我的问题吗?我已经使用没有访问参数模式的硬编码值测试了Execute_System过程,所以我不相信这是这个问题 .

2 回答

  • 0

    这是由于一个相当模糊的规则(RM 3.10.2(27ff)) . 但原因与实施困难有关 .

    当变量或参数的类型 access String 没有边界时,必须有一种方法来在使用变量或参数时获取边界:

    procedure Some_Procedure (A : access String) is 
         First, Last : Integer;
    begin
         First := A'First;
         Last := A'Last;
         ...
    end Some_Procedure;
    

    如果 A 本质上只是字符串第一个字符的地址,那么就无法计算 A'FirstA'Last .

    解决此问题的一种常用方法是将字符串的边界存储为字符串第一个字符之前的两个整数 . 然后,当 S'Access 用作 access String; 变量或参数的值时,代码知道字符串的第一个字符将以边界开头,因此它可以检索它们以获得 A'FirstA'Last 的值 .

    这个解决方案的问题是它意味着每个别名 String 必须存储这些边界 . (我认为这只是 aliased 对象的必要条件 . )如果你说的话

    S : aliased String(1..100);
    

    然后编译器必须生成边界,因为它无法判断程序中的某个点(可能甚至在不同的包中),代码可能会尝试使用 S'Access 作为 access String; 的值 . 即使 S'Access 从未像这样使用过,也必须存储这些边界,因为编译器不会成为主要目标 .

    妥协是判断如果别名 String S 没有作为类型的一部分的边界,那么将存储边界,并且您可以使用 S'Access 作为 access String . 如果别名 String 确实已将边界作为子类型的一部分,则不会存储边界,但您不能将 S'Access 用作 access String (如果边界匹配,您仍可以将其用作 access String(m..n) ) . 这意味着在这种情况下,存储边界:

    Input : aliased String := (1 .. 255 => ' ');
    

    但在这种情况下,他们不是:

    Input : aliased String(1 .. 255) := (others=> ' ');
    

    第一种形式是您可以在案例中使用的方法来解决问题 .

    如果Ada有办法编写第二种类型的声明但仍然告诉编译器将其视为第一种 - 即 . 存储边界并允许 'Access 可用作 access String . 事实上,我相信那里有一个Ada问题(我不希望未来版本的Ada提供解决方案 .

  • 5

    答案

    尝试将 Input'Access 更改为 Input'Unrestricted_Access .

    属性Unrestricted_Access

    属性 Unrestricted_Access 是GNAT的一个特性 . 它可以用于任何类型 .

    示例1

    注释的代码行不起作用 .

    type String_Access is access all String;
    
    function Str_10 return String is
       S : String (1 .. 10);
    begin
       return S;
    end;
    
    Str_1 : aliased String := "0123456789";
    Str_2 : aliased String := Str_10;
    Str_3 : aliased String (1 .. 10) := (others => '0');
    Str_Acc_1 : String_Access := Str_1'Access;
    Str_Acc_2 : String_Access := Str_2'Access;
    --Str_Acc_3 : String_Access := Str_3'Access;
    --Str_Acc_4 : String_Access := Str_3'Unchecked_Access;
    Str_Acc_5 : String_Access := Str_3'Unrestricted_Access;
    

    示例2

    正在行动demo .

    With Ada.Text_IO; Use Ada.Text_IO;  
    With Ada.Integer_Text_IO; Use Ada.Integer_Text_IO;
    
    procedure Program is
        type String_Access is access all String;
        Str_2 : aliased String (50 .. 60);
        Str_3 : aliased String (1 .. 10) := (others => '0');
        Str_Acc_5 : String_Access := Str_3'Unrestricted_Access;
    begin
        Put (Str_Acc_5'First);
        New_Line;
        Put (Str_Acc_5'Last);
        Str_Acc_5 := Str_2'Unrestricted_Access;
        New_Line;
        Put (Str_Acc_5'First);
        New_Line;
        Put (Str_Acc_5'Last);
    end Program;
    

    外部资源

    https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gnat_rm/Unrestricted_005fAccess.html

    Unrestricted_Access属性与Access类似,只是省略了所有辅助功能和别名视图检查 . 这是一个用户提防属性 . 它类似于Address,对于它来说,它是理想的替代品,其中所需的值是访问类型 . 换句话说,其效果与首先应用Address属性然后执行未选中的转换到所需的访问类型相同 . 在GNAT中,但不一定在其他实现中,对内层子程序使用静态链意味着应用于子程序的Unrestricted_Access产生的值只要子程序在范围内就可以调用(正常的Ada 95可访问性规则限制了此用法) ) . 可以对任何类型使用Unrestricted_Access,但如果它用于创建指向无约束对象的指针,则必须小心 . 在这种情况下,结果指针的范围与属性的上下文,可能不会返回到某个封闭范围 . 例如,函数不能使用Unrestricted_Access创建无约束指针,然后将该值返回给调用者 .

    评论

    @ajb在评论中表示,bouandery Str_Acc_5'First = Str_3'FirstStr_Acc_5'Last = Str_3'Last 可能是也可能不是 . 目前我还没有找到官方文件 . 另一方面boundary is matching on ideone.com .

相关问题