I'm instantiating a generic package with an enumeration to access one of multiple values and use in subprogram overloading. I want to have a well-defined, compile-time checked set of values I can use and look up.
generic
-- Different types because we don't want to ensure we never put
-- beer in a wine class, or wine in a beer stein. Our inventory
-- never changes, for... reasons.
type Wine is (<>);
type Beer is (<>);
package Bar is
type Wine_Glass is null record;
type Beer_Stein is null record;
-- Unopened cases/bottles of each.
type Wine_Inventory is array (Wine) of Natural;
type Beer_Inventory is array (Beer) of Natural;
procedure Pour (G : in out Wine_Glass; W : in Wine);
procedure Pour (S : in out Beer_Stein; B : in Beer);
end Bar;
What's the idiomatic to describe an empty enumeration?
with Bar;
procedure Wine_Tasting is
type Sampling_Wine is (Tempranillo, Zinfandel, Merlot);
pragma Unreferenced (Tempranillo, Zinfandel, Merlot);
type No_Beer is (None);
package Wine_Tasting_Bar is new Bar(Wine => Sampling_Wine, Beer => No_Beer);
Stein : Wine_Tasting_Bar.Beer_Stein;
begin
Wine_Tasting_Bar.Pour (Stein, None); -- legal!
end Wine_Tasting;
Is there a way to describe this such that Beer is an enumeration with no values, so that Pour can never been called with a Beer?
You have to declare an enumeration type with at least two values, and then declare a subtype with no values. You use the subtype to instantiate the generic:
type Wine_Kind is (Red, White, Green);
type Beer_Base is (Ale, Lager);
subtype No_Beer is Beer_Base range Lager .. Ale;
package Wine_Bar is new Bar (Wine => Wine_Kind, Beer => No_Beer);
According to Ada Reference Manual section 3.5.1 an enumeration type is described as
enumeration_type_definition ::= (enumeration_literal_specification
{, enumeration_literal_specification})
The first enumeration_literal_specification is required, further enumeration_literal_specifications are optional. From this syntax description I assert there is no way to declare a enumeration type with no enumeration_literal_specifications.
Related
Creating access type of array of integer gives an error. Below is my code, please help me to fix it.
procedure arry_point is
type arr is array(1..5) of integer;
obj:aliased arr;
type my_access1 is access all arr;
var1:my_access1:=obj'Access;-- this is good
ii: aliased array(1..5)of integer
type my_access is access all ii; --this is bad but how can i create access type for ii ?
var:my_access:=ii'access; ---?
begin
null;
end arry_point;
type My_Access is access ... what? The answer is, it has to be a type name (strictly a subtype_indication, see ARM 3.10(3).
When you say ii: aliased array(1..5) of integer you’re creating an array of an anonymous type; this means you can’t supply the type name to complete the access type definition.
You could imagine a language (C++?) in which you could say
type My_Access is access all Type_Of (II);
or, perhaps,
type My_Access is access all II'Type;
but neither of those is possible in Ada. I suspect the reason is that there wouldn’t be any point, because in Ada types are not equivalent even if they have the same structure:
1. procedure SG is
2. A : array (1 .. 5) of Integer := (others => 0);
3. B : array (1 .. 5) of Integer;
4. begin
5. B := A;
|
>>> expected type of B declared at line 3
>>> found type of A declared at line 2
6. end SG;
I am new to Ada programming.This is my ADA CODE for a program which gives me a list of things when typed a Football legend name in execution time.But I am getting the following errors.Please help me out:
Some of the Errors found are:
1.Discriminants must have a discrete or Access type
2.components "FBClubs cannot be used before end of record declaration
3.discriminant in a variant part must be of discrete part
4."player" is undefined
5."pos" is undefined.
6.no candidate interpretations match the sctuals : = > in call to inherited operation "to_string" at line "type playernames..."
with Ada.Text_Io; use Ada.Text_Io;
with Ada.Integer_Text_Io; use Ada.Integer_Text_Io;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
Procedure football_record is
type Position is (Goalkeeper,Midfielder,Forward,Defender);
type playernames is new Unbounded_String;
type FBClubs is (ACMilan,Man_United,Aresnal,ParisSt.Germain,Real_Madrid,Liverpool,Chelsea,Man_City,Lille,
Tottenham,Ajax,Juventus,Dortmund,Roma,Santos,Roma,Bayern_Munich,Inter_Milan);
type countries is (England,Argentina,Brazil,France,Italy,Portugal,Spain,Germany,Iran,Japan);
type fbplayer(player:playernames) is
record
WCAppearances:Integer;
pos:Position;
country:countries;
fbclubs:FBClubs;
case player is
when "David Beckham" =>
country:countries:=England;
WCAppearances:Integer:=3;
pos:Position:=Midfielder;
fbclubs:FBClubs:=ACMilan &"+" & Man_United &"+" & Real_Madrid &"+"& ParisSt.Germain;
when "Lionel Messi" =>
country:countries:=Argentina;
WCAppearances:Integer:=1;
pos:Position:=Forward;
fbclubs:FBClubs:=Barcelona;
.....and some other 12 players(legends)..
when others =>
country:countries:=Nil;
WCAppearances:Integer:=Nil;
pos:Position:=Nil;
fbclubs:FBClubs:=Nil;
end case;
end record;
begin
Get(player);
Put_Line(To_String(Integer'Image(player)));
Put_Line(To_String(Integer'Image(FBClubs'Image(fbclubs)));
Put_Line(To_Unbounded_String(Position'Image(pos)));
end football_record;
The biggest problem is that you're mixing code in with a type declaration.
In Ada, putting a case within a record is only for variant records; these are records where some fields exist in certain cases but not others. For example:
type Employee_Type is (Regular, Manager);
type Employee (Emp_Type : Employee_Type) is record
Name : Unbounded_String;
case Emp_Type is
when Manager =>
Managerial_Level : Integer;
when Regular =>
null;
end case;
end record;
This is a variant record; we're assuming here that there are two kinds of employees, and the Managerial_Level field makes sense only for one of those kinds. Records whose type is Regular will not have a Managerial_Level field at all.
This syntax isn't what you would use to return different values of fields. Instead, you need to do this in statements, usually in a procedure or function (or package initialization, or some other places that allow statements).
And since you're not using the variant record syntax, you don't need to make player a discriminant. It doesn't work, anyway; in Ada, a "discriminant" (like Emp_Type in my example) has to be a discrete type like an integer or an enumeration type (Employee_Type is an enumeration type), or an access (access discriminants are an advanced concept). It can't be an Unbounded_String. So you'd want to make player a regular field:
type fbplayer is record
player : Unbounded_String;
pos : Position;
country : countries;
clubs : FBClubs; -- Note name change!
WCAppearances : Integer;
end record;
and create a procedure to fill in the fields:
procedure Fill_In_Player(P : in out fbplayer; Player : Playernames) is
begin
P.Player := Player;
if Player = "David Beckham" then
P.country := England;
P.WCAppearances := 3;
P.pos = Midfielder;
P.clubs := ??? -- see below
elsif Player = "Lionel Messi" then
------- etc.
elsif ------
end if;
end Fill_In_Player;
and then call Fill_In_Player when you have the Player and want to set up the record fields. You have to write statements to do this; you can't do it inside the declaration of a record.
Note that in Ada, case statements can only be used on integer or enumeration types. You can't use them to test for a string, as some other languages allow.
Ada does not treat lower-case and upper-case letters the same in identifiers or keywords. Therefore, fbclubs is the same name as FBClubs, and you can't declare the field
fbclubs : FBClubs;
because of the name conflict. I changed the name.
Finally, it looks like you want FBClubs to hold more than one club. But FBClubs is an enumeration type, and can therefore hold only one value at a time. If you want each player record to contain a list of clubs, you'll need to do something else, such as using one of Ada's container types (like Ada.Containers.Vectors.Vector) or something like
type Set_Of_Clubs is array(FBClubs) of Boolean;
where each array value is true if the player played for that club.
I'm not sure that will take care of all your errors, but it looks like you have a lot of work to do already.
Can I have a vector filled with both, type A and B? I will fill it only with one type, so I am always sure, what I will get out of it. But it would make many things very easy for me, if I can define the vector once.
type T is tagged null record;
type A is new T with
record
x : Integer;
end record;
type B is new T with
record
y : Integer;
end record;
package Some_Vector is new Ada.Containers.Indefinite_Vectors (
Index_Type => Positive,
Element_Type => T <- ???
);
You can say:
package Some_Vector is new Ada.Containers.Indefinite_Vectors (
Index_Type => Positive,
Element_Type => T'Class
);
Some_Vector is now able to hold elements of type T or any type derived from it, including A or B. There's no requirement that all the elements have to be of the same type, as long as they're all derived from T; so if you don't really care whether this property is enforced, the above should work. If you really want the compiler to enforce that all elements are the same type, then you should simply declare two packages, A_Vector and B_Vector, for vectors of the two types; but then there's no way to write a "class-wide" type name that could refer to either an A_Vector or a B_Vector.
If you really want to combine both--have a vector type that could refer either to a vector of A or a vector of B, but still enforce that all elements of the vector have the same type, then I think this could be done if you define your own vector type and perform the needed check at run time, but it could get complicated. This compiles, but I haven't tested it:
generic
type Elem is new T with private;
package Sub_Vectors is
type Sub_Vector is new Some_Vector.Vector with null record;
overriding
procedure Insert (Container : in out Sub_Vector;
Before : in Some_Vector.Extended_Index;
New_Item : in T'Class;
Count : in Ada.Containers.Count_Type := 1)
with Pre => New_Item in Elem;
end Sub_Vectors;
package body Sub_Vectors is
procedure Insert (Container : in out Sub_Vector;
Before : in Some_Vector.Extended_Index;
New_Item : in T'Class;
Count : in Ada.Containers.Count_Type := 1) is
begin
Some_Vector.Insert
(Some_Vector.Vector(Container), Before, New_Item, Count);
end Insert;
end Sub_Vectors;
Unfortunately, you'd have to override every Insert and Replace_Element operation that could put an element into the vector. After you do all this, though, you can instantiate Sub_Vectors with Elem => A and with Elem => B, and the class Some_Vector.Vector'Class would be a class-wide type that would include both Sub_Vector types in the instance packages.
If you really want the compiler to enforce that all elements are the
same type, then you should simply declare two packages, A_Vector and
B_Vector, for vectors of the two types; but then there's no way to
write a "class-wide" type name that could refer to either an A_Vector
or a B_Vector.
You can, however, have a vector that points only to the A or B subtrees of the hierarchy:
type T is tagged null record;
type A is new T with null record;
type B is new T with null record;
type C is new T with null record;
type TAB is access all T'Class
with Dynamic_Predicate =>
TAB = null or else
(TAB.all in A'Class or TAB.all in B'Class);
Above yields the TAB Type which must be a [pointer to] an A'Class or B'Class, which you should be able to use in your vector. -- The only problem I've run into is you have to use GNAT's 'Unchecked_Access to get the access values of objects (due, I think, to my quick and dirty testing).
Hello Im new to ada and I am trying to create some kind of unconstrained array and I can't figure out how to do it in ada.
package data_puzzle is
type rotation is private;
type map(x_l,y_l,z_l : Natural) is private;
type valid_rotations is private;
private
type rotation is array(1..3) of Natural;
type map(x_l,y_l,z_l : Natural) is record
struct : Structure(x_l,y_l,z_l);
rot : rotation;
end record;
type valid_rotations is array(1..24) of map; --how can I make this row work?
end data_puzzle;
Structure looks like this
type structure(x_l,y_l,z_l : Natural) is record
structure : xyz(1..x_l,1..y_l,1..z_l);
X : Natural := x_l;
Y : Natural := y_l;
Z : Natural := z_l;
end record;
Basically I have a map with rotations and data. Then I want to store all the different rotations in a list of size 24. The only solution I have right now is to initiate
type valid_rotations is array(1..24) of map(x,y,z) then it works. But I do not want to initiate it like that since I do not know what the size will be at that moment.
Cheers!
Ok, the problem is that the type map can be of varying size -- the compiler cannot therefore simply set aside the proper amount of memory w/o further information -- so the solution is to create some sort of indirection which can be the element of an array: we have this type since Ada 83 but with Ada 2005 and on we can further constrain access types from being null.
-- I don't know what this is supposed to be; I'm assuming an array.
type xyz is Array(Positive Range <>, Positive Range <>, Positive Range <>)
of Integer;
type structure(x_l,y_l,z_l : Natural) is record
structure : xyz(1..x_l,1..y_l,1..z_l);
X : Natural := x_l;
Y : Natural := y_l;
Z : Natural := z_l;
end record;
package data_puzzle is
type rotation is private;
type map(x_l,y_l,z_l : Natural) is private;
type valid_rotations is private;
type map_handle is private;
private
type rotation is array(1..3) of Natural;
type map(x_l,y_l,z_l : Natural) is record
struct : Structure(x_l,y_l,z_l);
rot : rotation;
end record;
-- Because the size of the record "map" can change
-- depending on its discriminants, we cannot simply
-- make some array -- we can however have an array
-- of accesses (since we know their sizes at compile)
-- and constrain these access-types to be non-null.
type valid_rotations is array(1..24) of map_handle;
-- Here it is: an access which cannot be null.
type map_handle is Not Null Access Map;
end data_puzzle;
Also, I'd delete the _1 from the discriminants (X,Y,Z) look fine to me.
I want to declare an array with element-type of a variant record.
Something like this:
type myStruct (theType : vehicleType) is
record
...
when car => numOfWheels : Positive;
when others => null;
end record;
myArray : array (Positive range <>) of myStruct;
But in this case I get error.
It only allows this:
myArray : array (1.100) of myStruct(Car); //such as
So how to solve the index-problem and how to describe an array of a variant record's type without giving it's discriminant?
The example above will not compile. Here is a correct version (I changed mystruct to Integer for simplicity):
procedure test_array is
subtype Vehicle_Array_Index is Integer range 1..100; --// Maximum size is 100
type Arr_T is array (Vehicle_Array_Index range <>) of Integer;
type Vehicle_Array (Size: Vehicle_Array_Index := 1) is record
Vehicles : Arr_T(1..Size);
end record;
begin
null;
end;
One of the errors was that you cannot have anonymous arrays inside records, and second, you should use the discriminant to constrain the array inside.
As mentioned above this is not a good solution for varying-length arrays as most likely you will get a maximum sized array anyway. If you want arrays with dynamically determined sizes you can use blocks for that.
declare
a: array(1..n) of integer; -- n can be a variable here
begin
--some code using a
end;
It also works in the declaration parts of procedures and functions where n can be a parameter passed to the subprogram (one of the advantages Ada has over C/C++). And of course you can just allocate arrays dynamically on the heap using allocators.
If you want to be able to create objects of a discriminated type and change (or figure out) what the type is at runtime, you have to give the discriminant a default value in the declaration.
The reason for this is that Ada doesn't want to have to worry about dealing with uninitialized discriminated objects that it can't even figure out the size and valid record fields of.
For this reason, and some reasons I go into a bit in the comments, Ada discriminated records aren't actually very useful in mixed language programming (eg: exactly duplicating a C union). They can be handy on their own terms though.
For the example you give, you'd do the following (warning: Not compiled):
type myStruct (theType : vehicleType := car) is
record
...
when car => numOfWheels : Positive;
when others => null;
end record;
Then you could set one of the array values at runtime thusly:
myArray(20) := (theType => car,
field1 => myArray(20).field1,
... , --// Fill in the rest of the fields by name
numberOfWheels => 4);
As for this part:
myArray : array (Positive range <>) of myStruct;
You cannot declare actual array objects with an indeterminate range like this. You could declare a type that way, but an object has to have an actual size. If you want an array of varying length, you can once again use a variant record. (Again, not compiled)
subtype Vehicle_Array_Index is Integer range 1..100; --// Maximum size is 100
type Vehicle_Array (Vehicle_Array_Index : Size := 1) is record
Vehicles : array (Vehicle_Array_Index range <>) of myStruct;
end record;
Oh, and one more thing. You aren't doing this in your example, but if you ever want to use your discriminant to size an array like above, beware. When you declare objects of that type (again, assuming you used a default for the discriminant), the compiler will try to reserve enough space for the largest possible size you could ever feed it a value for. That makes it a Very Bad Idea to make a discriminated array indexed by something with a huge range like Integer or Positive. I know computers are bigger these days, but still most folks don't have 4 gig to spare for one silly little array. So if you are using your discriminant as an array index, it would be a good idea to create a smaller subtype of Integer to use for the type of the discriminant.