繁体   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