簡體   English   中英

如何將對約束字符串的訪問權限傳遞給Ada中的子程序

[英]How to pass access to a constrained string to a subprogram in Ada

好的。 我真的很討厭這樣簡單的問題,但我已經完全閱讀了三本不同的書籍,向我解釋了訪問類型和參數模式,我無法理解我做錯了什么。

我在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)的解決方案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;

如果A基本上只是字符串第一個字符的地址,那么就沒有辦法計算A'FirstA'Last

解決此問題的一種常用方法是將字符串的邊界存儲為字符串第一個字符之前的兩個整數。 然后,當S'Access用作access String;的值時access String; 變量或參數,代碼知道字符串的第一個字符將以邊界A'First ,因此它可以檢索它們以獲得A'FirstA'Last的值。

這個解決方案的問題是它意味着每個別名的String必須存儲這些邊界。 (我認為只有aliased對象才有必要。)如果你說

S : aliased String(1..100);

然后編譯器必須生成邊界,因為它無法判斷程序中的某個點(可能甚至在不同的包中),代碼是否可能嘗試使用S'Access作為access String;的值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可能會提供解決方案。

回答

嘗試將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

工作演示在行動中。

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可能是也可能不是。 目前我還沒有找到官方文件。 另一方面, 邊界在ideone.com上匹配

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM