[英]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: 您要做的是:
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.