简体   繁体   English

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

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

I'll start with the classic example of a generic procedure in 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;

Now suppose I want to implement a specialized String_Swap procedure for swapping strings, and provide it to all users of my package. 现在,假设我想实现一个专门的String_Swap过程来交换字符串,并将其提供给我的包的所有用户。 I can add the following to the body declaration in swaps.adb : 我可以在swaps.adb的主体声明中添加以下swaps.adb

procedure String_Swap is new Generic_Swap (String);

However, if I add nothing to the specification in swaps.ads , then no package can use this procedure. 但是,如果我在swaps.ads未在规范中添加任何swaps.ads ,则没有软件包可以使用此过程。 For example: 例如:

-------------------------
--  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;

I've tried to add the type of the procedure in to the specification: 我试图将过程的类型添加到规范中:

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

but then Ada complains that this specification has a missing body and that the definition in swaps.adb conflicts with it. 但随后Ada抱怨说此规范的正文缺失,并且swaps.adb中的定义与此规范冲突。

The way I would address this is to use a child package: 我要解决的方法是使用子程序包:

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;

Note that it's possible to write a generic that will handle indefinite types: 请注意,可以编写一个处理不确定类型的泛型:

generic
   type Element (<>) is private;

and change the body of Generic_Swap to 并将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;

but to use it the actual objects must either be unconstrained or have the same subtype. 但要使用它,实际对象必须不受约束或具有相同的子类型。

The only thing that a user of Swaps can see is the spec. Swaps的用户唯一能看到的就是规格。 Since there is nothing about String_Swap in the spec, no amount of fiddling in the package body will make any difference. 由于规范中与String_Swap无关,因此包主体中的摆弄不会有任何不同。

If you wanted to "implement a specialized String_Swap procedure for swapping strings", you would have to include it in the spec: 如果要“实现用于交换字符串的专用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;

This turns out to be a bad example: when compiled with -gnatl , we get 原来这是一个不好的例子:用-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;

This is because the type String is indefinite, that is, a particular String has a particular length, and can only be assigned to another String (or slice of a String ) of the same length; 这是因为类型String是不确定的,也就是,特定的String具有特定的长度,并且只能分配到另一个String (或切片String )具有相同的长度; so even if your procedure Main was written out without using the generic, it would fail with a constraint error at runtime. 因此,即使您的过程Main在不使用泛型的情况下被写出,它也会在运行时因约束错误而失败。 Check out Ada.Strings.Unbounded at ARM A.4.5 . ARM A.4.5处查看Ada.Strings.Unbounded

So, try with a definite type: 因此,尝试使用确定的类型:

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;

Unfortunately, 不幸,

 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;

The solution has to be to instantiate separately: perhaps at library level, 解决方案必须分别实例化:也许在库级别,

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

It's going to be a lot easier to leave it up to your users to instantiate the generic as they wish. 让用户根据需要实例化泛型将变得容易得多。

You can't use your generic for type String because it is an unconstrained type. 您不能将泛型用于String类型,因为它是不受限制的类型。 But lets use Ada.Strings.Unbounded.Unbounded_String instead. 但是让我们改用Ada.Strings.Unbounded.Unbounded_String

What you have to do is: 您要做的是:

  1. Publish a specification of a suitable procedure in the package specification. 在包装规范中发布合适程序的规范。
  2. Implement the public procedure with a call to the internally instantiated generic procedure. 通过调用内部实例化的通用过程来实现公共过程。
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;

But in general, I prefer to keep instantiations of generics completely separate from the specifications and implementations of those generics. 但是总的来说,我更喜欢将泛型的实例与这些泛型的规范和实现完全分开。

I've tried to add the type of the procedure in to the specification: 我试图将过程的类型添加到规范中:

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

but then Ada complains that this specification has a missing body and that the definition in swaps.adb conflicts with it. 但随后Ada抱怨说此规范的正文缺失,并且swaps.adb中的定义与此规范冲突。

This is good idea, but instantiation can't complete a procedure declaration (and compiler says that). 这是个好主意,但是实例化无法完成过程声明(编译器会这样说)。 What you need is use a procedure renaming: 您需要使用一个过程重命名:

   procedure String_Swap_Inst is new Generic_Swap (String);

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

Also, to be able to use String with your generic, you need change it a little to allow unconstrained types: 另外,为了能够将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;

And of course you can swap this way only string of equal length, otherwise you will get a Constraint_Error just like in an assignment: 当然,您可以通过这种方式只交换长度相等的字符串,否则您将获得Constraint_Error,就像在分配中一样:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM