好。我真的很讨厌这样简单的问题,但我已经完全阅读了三本不同的书籍,向我解释了访问类型和参数模式,我无法理解我做错了什么。
我正在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,而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过程,所以我不相信这是这个问题。
答案 0 :(得分:5)
这是由于一个相当模糊的规则(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'First
和A'Last
。
解决此问题的一种常用方法是将字符串的边界存储为字符串第一个字符前面的两个整数。然后,当S'Access
用作access String;
变量或参数的值时,代码知道字符串的第一个字符将以边界开头,因此它可以检索它们以获得值A'First
和A'Last
。
此解决方案存在的问题是,它意味着每个别名String
必须存储这些边界。 (我认为只有aliased
个对象才有必要。)如果你说
S : aliased String(1..100);
然后编译器必须生成边界,因为它无法判断程序中的某个点(可能甚至在不同的包中),代码是否可能尝试使用S'Access
作为值access String;
。即使S'Access
从未像这样使用过,也必须存储这些边界,因为编译器将无法预测将来可能会执行哪些代码。这会导致浪费空间。这不是一件好事,因为嵌入式系统是Ada的主要目标之一。
妥协是判断如果别名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可能会提供解决方案。
答案 1 :(得分:0)
尝试将Input'Access
更改为Input'Unrestricted_Access
。
属性Unrestricted_Access
是GNAT的一项功能。它可以用于任何类型。
注释的代码行不起作用。
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;
正在运作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'First
和Str_Acc_5'Last = Str_3'Last
可能是也可能不是。目前我还没有找到官方文件。另一方面是boundary is matching on ideone.com。