使用泛型参数丢失信息

时间:2012-07-04 14:50:40

标签: generics ada

泛型参数的声明并不足以提供类型关系(子类型),而且这些信息只是丢失了......例如:

-- generic_p.ads
generic
   type Index_Range_Type is range <>;
   type Count_Range_Type is range <>;
procedure Generic_P (I : Index_Range_Type, C : Count_Range_Type);

-- generic_p.adb
procedure Generic_P (I : Index_Range_Type, C : Count_Range_Type) is
begin
   if I = C then -- oops : cannot compare different types...
     -- ...
   end if;
end Generic_P;

-- main.adb
procedure Main is
   type Index_Range_Type is 0 .. 512;
   subtype Count_Range_Type is Index_Range_Type range 1 .. Index_Range_Type'Last;

   procedure P is new Generic_P (Index_Range_Type, Count_Range_Type);

   I : Index_Range_Type := 33;
   C : Count_Range_Type := 42;
begin
   if I = C then -- Ok : Count_Range is a subset of Index_Range, they can be compared
      -- ...
   end if;
   P (I, C);
end Main;

这为generic_p.adb中的比较提供了以下错误:invalid operand types [...] left operand has type "Index_Range_Type" [...] right operand has "type Count_Range_Type"。子通配在通用程序中不可见。

有没有办法指定通用参数之间的关系?

更多信息

我真的需要Count_Range_Type作为过程的参数,以便能够添加另一个需要Count_Range_Type的参数。

-- generic_p.ads
generic
   type Index_Range_Type is range <>;
   type Count_Range_Type is range <>;
   with procedure F (C : Count_Range_Type);
procedure Generic_P (I : Index_Range_Type, C : Count_Range_Type);

我无法直接使用该类型,我需要P绝对通用且独立。

4 个答案:

答案 0 :(得分:3)

离开我的头顶,或许反过来谁宣布可能满足你的需要。考虑使用泛型定义子类型:

generic
   type Index_Range_Type is range <>;
package Generic_Provider is

   pragma Assert(Index_Range_Type'First = 0);
   subtype Count_Range_Type is Index_Range_Type range 1 .. Index_Range_Type'last;

   procedure P (I : Index_Range_Type; C : Count_Range_Type);

end Generic_Provider;

身体:

package body Generic_Provider is

   procedure P (I : Index_Range_Type; C : Count_Range_Type) is
   begin
      if I = C then -- No problem comparing now...
            -- ...
         null;
      end if;
   end P;
end Generic_Provider;

然后在你的实例化单元中:

with Generic_Provider;

procedure Main is
   type Index_Range_Type is range 0 .. 512;
   package P_Provider is new Generic_Provider (Index_Range_Type);

   subtype Count_Range_Type is P_provider.Count_Range_Type;


   I : Index_Range_Type := 33;
   C : Count_Range_Type := 42;
begin
   if I = C then -- Ok : Count_Range is a subset of Index_Range, and all is good.
         -- ...
      null;
   end if;
   P_Provider.P (I, C);
end Main;

这类似于System.Address_To_Access_Conversions采用的方法,其中泛型用“基础”类型实例化,然后泛型提供“增强” - 在该包中它是一种访问类型,在此将是子类型。

答案 1 :(得分:3)

您还可以使用函数“=”(左:Index_Range_Type;右:Count_Range_Type)将布尔值返回到通用标题...强制用户提供适当的等于函数。

需要该路由,因为现在通用标头,编译器可能不会假设他有两种类型相关;签名没有提供信息。

答案 2 :(得分:3)

这解决了原始问题的“进一步信息”部分,其中通用需要使用包含实例类型的子类型的参数的过程进行实例化。

本质上,使用通用包来设置子类型,然后通用子包提供所需的过程(使用通用的正式过程进行实例化)。不可否认,这是解决问题的一个相当复杂的解决方案。所以我们走了:

创建子类型的“父”泛型:

generic
   type Index_Range_Type is range <>;
package Generic_Provider is
   pragma Assert(Index_Range_Type'First = 0);
   subtype Count_Range_Type is Index_Range_Type range 1 .. Index_Range_Type'last;

end Generic_Provider;

所有这一切都是声明子类型,它不需要任何主体(实际上一个主体是非法的)。

这是我们的程序提供程序的规范,它使用客户端提供的正式程序。

generic
   with procedure F(I : Index_Range_Type;
                    C : Count_Range_Type);

package Generic_Provider.Services is

   procedure P (I : Index_Range_Type; C : Count_Range_Type);

end Generic_Provider.Services;

只是为了咧嘴,它的主体,它验证可以调用正式程序并且子类型比较是有效的:

package body Generic_Provider.Services is

   procedure P (I : Index_Range_Type; C : Count_Range_Type) is
   begin
      if I = C then
         F(I, C);
      end if;
   end P;

end Generic_Provider.Services;

最后,实例化主程序:

with Generic_Provider.Services;

procedure Main is
   type Index_Range_Type is range 0 .. 512;
   package Type_Provider is new Generic_Provider (Index_Range_Type);
   subtype Count_Range_Type is Type_Provider.Count_Range_Type;

   procedure My_F (I : Index_Range_Type;
                   C : Count_Range_Type) is
   begin
      null;
   end My_F;

   package P_Provider is new Type_Provider.Services(My_F);

   I : Index_Range_Type := 33;
   C : Count_Range_Type := 42;
begin
   if I = C then
      null;
   end if;
   P_Provider.P (I, C);
end Main;

答案 3 :(得分:2)

您可以使用完整类型而不是子类型:

-- generic_p.ads
generic
   type Index_Range_Type is range <>;
   type Count_Range_Type is new Index_Range_Type;
procedure Generic_P (I : Index_Range_Type; C : Count_Range_Type);

-- generic_p.adb
procedure Generic_P (I : Index_Range_Type; C : Count_Range_Type) is
begin
   -- Neccessary conversion
   if I = Index_Range_Type (C) then
     -- ...
   end if;
end Generic_P;

-- main.adb
procedure Main is
   type Index_Range_Type is 0 .. 512;
   type Count_Range_Type is new Index_Range_Type range 1 .. Index_Range_Type'Last;

   procedure P is new Generic_P (Index_Range_Type, Count_Range_Type);

   I : Index_Range_Type := 33;
   C : Count_Range_Type := 42;
begin
   if I = Index_Range_Type (C) then
      -- ...
   end if;
   P (I, C);
end Main;

也可以将C转换为Index_Range_Type而不指定generic部分中的类型之间的关系。这样,您仍然可以使用您的子类型,但Count_Range_Type无法转换为Index_Range_Type的通用过程的任何实例都会引发异常。

我建议您阅读Ada 95 Quality and Style Guide, 8.2.4以获取有关使用泛型的子类型的更多信息。