簡體   English   中英

如何編寫通用實例的規范?

[英]How do I write the specification for a generic instantiation?

我將從Ada中通用過程的經典示例開始:

-------------------------
--  swaps.ads
-------------------------
package Swaps is
  generic
    type E is private;
  procedure Generic_Swap (Left, Right : in out E);
end Swaps;
-------------------------
--  swaps.adb
-------------------------
package body Swaps is
  procedure Generic_Swap (Left, Right : in out E) is
    Temporary : E;
  begin
    Temporary := Left;
    Left := Right;
    Right := Temporary;
  end Generic_Swap;
end Swaps;

現在,假設我想實現一個專門的String_Swap過程來交換字符串,並將其提供給我的包的所有用戶。 我可以在swaps.adb的主體聲明中添加以下swaps.adb

procedure String_Swap is new Generic_Swap (String);

但是,如果我在swaps.ads未在規范中添加任何swaps.ads ,則沒有軟件包可以使用此過程。 例如:

-------------------------
--  main.adb
-------------------------
with Swaps; use Swaps;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
   First  : String := "world!";
   Second : String := "Hello, ";
begin
   String_Swap (First, Second); -- #Error: String_Swap is undefined#
   Put_Line (First);
   Put_Line (Second);
end Main;

我試圖將過程的類型添加到規范中:

procedure String_Swap (Left, Right : in out String);

但隨后Ada抱怨說此規范的正文缺失,並且swaps.adb中的定義與此規范沖突。

我要解決的方法是使用子程序包:

with Ada.Strings.Unbounded;

package Swaps.Instances is
   procedure Swap is new Generic_Swap (Element => Character);
   procedure Swap is new Generic_Swap (Element => Ada.Strings.Unbounded.Unbounded_String;
   ...
end Swaps.Instances;

請注意,可以編寫一個處理不確定類型的泛型:

generic
   type Element (<>) is private;

並將Generic_Swap的主體Generic_Swap

procedure Generic_Swap (Left, Right : in out Element) is
   Temp : constant Element := Left;
begin -- Generic_Swap
   Left := Right;
   Right := Temp;
end Generic_Swap;

但要使用它,實際對象必須不受約束或具有相同的子類型。

Swaps的用戶唯一能看到的就是規格。 由於規范中與String_Swap無關,因此包主體中的擺弄不會有任何不同。

如果要“實現用於交換字符串的專用String_Swap過程”,則必須將其包括在規范中:

package Swaps is
   generic
      type E is private;
   procedure Generic_Swap(Left, Right : in out E);
   procedure String_Swap is new Generic_Swap(String);
end Swaps;

原來這是一個不好的例子:用-gnatl編譯時,我們得到

 1. package Swaps is
 2.    generic
 3.       type E is private;
 4.    procedure Generic_Swap(Left, Right : in out E);
 5.    procedure String_Swap is new Generic_Swap(String);
                                                 |
    >>> actual for "E" must be a definite subtype

 6. end Swaps;

這是因為類型String是不確定的,也就是,特定的String具有特定的長度,並且只能分配到另一個String (或切片String )具有相同的長度; 因此,即使您的過程Main在不使用泛型的情況下被寫出,它也會在運行時因約束錯誤而失敗。 ARM A.4.5處查看Ada.Strings.Unbounded

因此,嘗試使用確定的類型:

package Swaps is
   generic
      type E is private;
   procedure Generic_Swap(Left, Right : in out E);
   procedure Character_Swap is new Generic_Swap(Character);
end Swaps;

不幸,

 1. package Swaps is
 2.    generic
 3.       type E is private;
 4.    procedure Generic_Swap(Left, Right : in out E);
 5.    procedure Character_Swap is new Generic_Swap(Character);
       |
    >>> warning: cannot instantiate "Generic_Swap" before body seen
    >>> warning: Program_Error will be raised at run time

 6. end Swaps;

解決方案必須分別實例化:也許在庫級別,

with Swaps;
procedure Character_Swap is new Swaps.Generic_Swap(Character);

讓用戶根據需要實例化泛型將變得容易得多。

您不能將泛型用於String類型,因為它是不受限制的類型。 但是讓我們改用Ada.Strings.Unbounded.Unbounded_String

您要做的是:

  1. 在包裝規范中發布合適程序的規范。
  2. 通過調用內部實例化的通用過程來實現公共過程。
with Ada.Strings.Unbounded;

package Swaps is
   generic
      type Element_Type is private;
   procedure Generic_Swap (Left, Right : in out Element_Type);

   procedure Swap (Left, Right : in out Ada.Strings.Unbounded.Unbounded_String);
end Swaps;
package body Swaps is

   procedure Generic_Swap (Left, Right : in out Element_Type) is
      Temporary : Element_Type;
   begin
      Temporary := Left;
      Left      := Right;
      Right     := Temporary;
   end Generic_Swap;

   procedure Swap_Unbounded_Strings is
     new Generic_Swap (Element_Type => Ada.Strings.Unbounded.Unbounded_String);

   procedure Swap (Left, Right : in out Ada.Strings.Unbounded.Unbounded_String) is
   begin
      Swap_Unbounded_Strings (Left  => Left,
                              Right => Right);
   end Swap;
end Swaps;

但是總的來說,我更喜歡將泛型的實例與這些泛型的規范和實現完全分開。

我試圖將過程的類型添加到規范中:

 procedure String_Swap (Left, Right : in out String); 

但隨后Ada抱怨說此規范的正文缺失,並且swaps.adb中的定義與此規范沖突。

這是個好主意,但是實例化無法完成過程聲明(編譯器會這樣說)。 您需要使用一個過程重命名:

   procedure String_Swap_Inst is new Generic_Swap (String);

   procedure String_Swap (Left, Right : in out String)
     renames String_Swap_Inst;

另外,為了能夠將String與泛型一起使用,您需要對其進行一些更改以允許不受約束的類型:

package Swaps is
   generic
      type E (<>) is private;
   procedure Generic_Swap (Left, Right : in out E);

   procedure String_Swap (Left, Right : in out String);
end Swaps;

package body Swaps is
  procedure Generic_Swap (Left, Right : in out E) is
    Temporary : E := Left;
  begin
    Left := Right;
    Right := Temporary;
   end Generic_Swap;

   procedure String_Swap_Inst is new Generic_Swap (String);

   procedure String_Swap (Left, Right : in out String)
     renames String_Swap_Inst;
end Swaps;

當然,您可以通過這種方式只交換長度相等的字符串,否則您將獲得Constraint_Error,就像在分配中一樣:

X : String (1 .. 2) := "123";  --  Constraint_Error!!!

暫無
暫無

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

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