Reputation: 5661
The declaration of generic parameters is not exhaustive enough to give type relationships (subtypes) and this information is simply lost... For example :
-- 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;
Which gives the following error for the comparison in generic_p.adb : invalid operand types [...] left operand has type "Index_Range_Type" [...] right operand has "type Count_Range_Type"
. The subtyping is not visible in the generic procedure.
Is there any way to specify the relationship between generic parameters ?
I really need Count_Range_Type
as a parameter of the procedure to be able to add another parameter which needs 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);
I can't directly use the type, I need P to be absolutely generic and independent.
Upvotes: 3
Views: 124
Reputation: 8522
This addresses the "Further Informations" portion of the original question, whereby a generic needs to be instantiated with a procedure containing a parameter that's a subtype of the instantiating type.
Essentially, a generic package is employed to set up the subtype, and then a generic child package provides the desired procedure (which is instantiated with the generic formal procedure). Admittedly, this is a rather involved solution to the problem. So here we go:
The "parent" generic that creates the subtype:
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;
All this does is declare the subtype, it needs no body (and in fact a body would be illegal).
Here's the spec of our procedure provider, which utilizes a client-supplied formal procedure.
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;
Just for grins, its body, which verifies that the formal procedure can be invoked and that the subtype comparison is valid:
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;
Finally, the instantiating main program:
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;
Upvotes: 3
Reputation: 39768
You can use a full type instead of a subtype:
-- 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;
It is also possible to just cast C
to Index_Range_Type
without specifying the relation between the types in the generic
section. That way, you can still use your subtype, but any instance of the generic procedure where Count_Range_Type
is not castable to Index_Range_Type
will raise an exception.
I suggest reading Ada 95 Quality and Style Guide, 8.2.4 for more information about using subtypes with generics.
Upvotes: 2
Reputation: 4198
You could also put a with function "=" (Left : Index_Range_Type; Right : Count_Range_Type) Return boolean into the generic's header... that forces the user to provide an appropriate equals function.
That route is needed because as the generic header now stands, the compiler may NOT make the assumption he two types are related; that is not given from the information is the signature.
Upvotes: 3
Reputation: 8522
Off the top of my head, perhaps inverting who declares what might meet your need. Consider having the generic define the subtype:
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;
In the body:
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;
Then in your instantiating unit:
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;
This is akin to the approach System.Address_To_Access_Conversions takes, whereby the generic is instantiated with a "foundational" type, and the generic then provides the "enhancements"--in that package it's an access type, in this it would be the subtype.
Upvotes: 3