好的.我真的很讨厌这样简单的问题,但我已经完全阅读了三本不同的书籍,向我解释了访问类型和参数模式,我无法理解我做错了什么.
我在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;
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
我不明白为什么我不能访问这个字符串,因为它有明确的界限.我已经看到了一个解决方案new subtype Command_Access is access all String(1..255),但我不明白为什么这是一个解决方案(同时原谅语法错误,我仍然是新的子类型).
有人可以解释我的问题吗?我已经使用没有访问参数模式的硬编码值测试了Execute_System过程,所以我不相信这是这个问题.
这是由于一个相当模糊的规则(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;
Run Code Online (Sandbox Code Playgroud)
如果A基本上只是字符串的第一个字符的地址,那么将无法计算A'First和A'Last.
解决此问题的一种常用方法是将字符串的边界存储为字符串第一个字符之前的两个整数.然后,当S'Access被用作值access String;变量或参数,代码知道该字符串的第一个字符将被边界前面,因此可以检索它们来获得的值A'First和A'Last.
这个解决方案的问题是它意味着每个别名都String必须存储这些边界.(我认为这只是aliased对象所必需的.)如果你说
S : aliased String(1..100);
Run Code Online (Sandbox Code Playgroud)
然后编译器必须生成边界,因为它无法判断程序中的某个点(可能甚至在不同的包中),代码是否可能尝试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 => ' ');
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,他们不是:
Input : aliased String(1 .. 255) := (others=> ' ');
Run Code Online (Sandbox Code Playgroud)
第一种形式是您可以在案例中使用的方法来解决问题.
如果Ada有办法编写第二种类型的声明但仍然告诉编译器将其视为第一种 - 即存储边界并允许其'Access作为一种可用方式,这将是很好的access String.事实上,我相信有一个Ada问题(我不想抬头)提出一个可能的语法.我记得,有一些关于几种可能的语法的讨论并且它们都很丑陋,所以问题被删除了,但是未来版本的Ada可能会提供解决方案.