如何编写通用实例的规范?

时间:2018-02-23 21:10:13

标签: generics ada

我将从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中的正文声明中:

procedure String_Swap is new Generic_Swap (String);

但是,如果我在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中的定义与它相冲突。

3 个答案:

答案 0 :(得分:4)

我要解决这个问题的方法是使用子包:

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的正文更改为

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

但要使用它,实际对象必须是不受约束的或具有相同的子类型。

答案 1 :(得分:3)

Swaps的用户唯一可以看到的是规范。由于规范中没有String_Swap,因此包 body 中的任何数量都不会产生任何差异。

如果你想实现一个专门用于交换字符串&#34;的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的切片1}})长度相同;因此,即使您的过程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);

让用户根据自己的意愿实例化通用设置会更容易。

答案 2 :(得分:3)

您不能将您的通用用于类型String,因为它是一种不受约束的类型。但是让我们改用Ada.Strings.Unbounded.Unbounded_String

你要做的是:

  1. 在包规范中发布合适程序的规范。
  2. 通过调用内部实例化的通用过程来实现公共过程。
  3. 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;
    

    但总的来说,我更喜欢将泛型的实例化与这些泛型的规范和实现完全分开。