如何编写通用实例化的规范?
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
中的正文声明中:
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
中的定义与之冲突。
Swaps
的用户唯一能看到的就是规范。由于规范中没有关于 String_Swap
的任何内容,因此包 body 中的任何摆弄都不会产生任何影响。
如果您想 "implement a specialized String_Swap
procedure for swapping strings",您必须将其包含在规范中:
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
。
您需要做的是:
- 在包规范中发布合适程序的规范。
- 通过调用内部实例化的通用过程来实现 public 过程。
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;
但总的来说,我更喜欢将泛型的实例化与这些泛型的规范和实现完全分开。
我要解决这个问题的方法是使用子包:
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;
但要使用它,实际对象必须不受约束或具有相同的子类型。
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.
这是个好主意,但实例化无法完成过程声明(编译器这么说)。您需要的是使用重命名过程:
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!!!
我将从 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
中的定义与之冲突。
Swaps
的用户唯一能看到的就是规范。由于规范中没有关于 String_Swap
的任何内容,因此包 body 中的任何摆弄都不会产生任何影响。
如果您想 "implement a specialized String_Swap
procedure for swapping strings",您必须将其包含在规范中:
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
。
您需要做的是:
- 在包规范中发布合适程序的规范。
- 通过调用内部实例化的通用过程来实现 public 过程。
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;
但总的来说,我更喜欢将泛型的实例化与这些泛型的规范和实现完全分开。
我要解决这个问题的方法是使用子包:
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;
但要使用它,实际对象必须不受约束或具有相同的子类型。
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.
这是个好主意,但实例化无法完成过程声明(编译器这么说)。您需要的是使用重命名过程:
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!!!