Ada unconstrained type - ada

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.

Related

Ada record with constant access-to-object-of-parent-type

I recently started learning Ada. I want to see if there's a possibility in creating a Boost::Statechart-like framework in Ada. To do this I need a record structure with a constant access-to-object-of-parent-type component, like a tree node that statically points to another tree node, and the parent pointer must not be changed at all times. Something like this:
-- Not working sample
type Node_T is record
Parent : constant access Node_T;
-- error: constant components are not permitted
end record;
-- I wish to create objects of this type like this
Top_Node : Node_T (null);
Child1_Node : Node_T (Top_Node'Access);
Child2_Node : Node_T (Top_Node'Access);
It seems that constant member fields are not supported in Ada. So I resorted to using access discriminants:
-- Not working sample
type Node_T (Parent : access Node_T) is null record;
-- error: type declaration cannot refer to itself
However, using named-access-type as discriminant works
type Node_T;
type Ref_Node_T is access all Node_T;
type Node_T (Parent : Ref_Node_T) is null record;
However, from what I learned this causes the life-time of Node_T objects to be bound to that of a Ref_Node_T object, rather than another parent Node_T object. Is this true?
Are there any better ways of implementing what I need?
An alternate approach to creating a finite state machine is described in https://www.sigada.org/ada_letters/june2000/sanden.pdf
This solution uses a combination of protected objects and tasks to implement the finite state machine.
An alternate alternate solution for FSM is to use enumerations and arrays, and if you're going to need more than one, generic.
Generic
Type State is (<>); -- Any discrete type.
Type Event is (<>);
Package Finite_State_Machine_Domain is
Type Domain is Array(State, Event) of State;
Generic
Start,
Error : State;
Package Finite_State_Machine is
Type State_Machine is private;
Function Create (State_Map : Domain) return State_Machine;
Function Get_State (Object : in State_Machine) return State;
Procedure Send_Event(Object : in out State_Machine; Transition : in Event);
Private
Type State_Machine is record
Current : State := Start;
State_Map : Domain := (Others => Error);
End record;
End Finite_State_Machine;
End Finite_State_Machine_Domain;
Package Body Finite_State_Machine_Domain is
Package Body Finite_State_Machine is
Function Create (State_Map : Domain) return State_Machine is
( State_Machine'(State_Map => State_Map, Others => <>) );
Function Get_State (Object : in State_Machine) return State is
( Object.Current );
Procedure Send_Event(Object : in out State_Machine; Transition : in Event) is
Begin
if Object.Current /= Error then
Object.Current:= Object.State_Map(Object.Current, Transition);
end if;
End Send_Event;
End Finite_State_Machine;
End Finite_State_Machine_Domain;

Ada - access constant confusion

Say I have a type:
type A;
type XA is access constant A;
type A is
record
Member : Natural := 1;
Neighbor : XA;
end record;
I'm confused about the access constant part.
If I instantiate a instance of XA that points to a instance of A, what can I change while only holding the reference to the XA "instance" ?
Can I change the member of the object that XA points to ? I'd say no, but what about the Neighbor of the A in the XA object ?
Can someone explain the use of access constant to me ?
Here's a small example showing what it does:
procedure Access_Constant is
type XA is access constant Integer;
A : aliased Integer;
X : XA;
begin
X := A'Access;
X.all := 4;
end Access_Constant;
When you attempt to compile it, the assignment to X works fine (X is a variable), while the assignment to X.all is forbidden (as X.all is a constant - per the definition of XA).
Although XA is declared to point to a constant Integer, even a variable is acceptable, but you can only treat a dereference of an XA entity as a constant Integer, even if the object is a variable.

Can’t create access type to access an array of integer

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;

Return a fat/thick pointer as an out parameter

I am having trouble creating a thick pointer. My current set of declarations look like this:
type Index_Typ is mod 20; -- will be larger in real life
type Data_Buffer_Typ is array (Index_Typ range <>) of Integer; --unconstrained array type
type Data_Buffer_Ptr is access all Data_Buffer_Typ; -- a thick pointer, contains the bounds of array subtype pointed to and address..
Data_Buffer : aliased Data_Buffer_Typ (Index_Typ) := (others => 0); -- this is private
type Result_Typ is (Ok, Overflow, Null_Pointer);
procedure Retrieve (Index : in Index_Typ;
Len : in Index_Typ;
Data_Ptr : out Data_Buffer_Ptr;
Result : out Result_Typ) is
begin
-- assuming range checks are ok, what goes here ?
end Retrieve;
so if i declare:
Ptr : Data_Buffer_Ptr := null;
and given a call of Retreive (2,3, Ptr,Result); how do i end up with a pointer that points at elements 2,3 & 4 of Data_Buffer ?
Notes:
Yes i know passing out an array slice will probably be done as a
pointer anyway, but we want to explicitly use pointers, not
implicitly (and not my choice!).
Yes i have experimented, i usually get : (object subtype must statically match designated subtype) error message..
Where possible use of new to be avoided.
This works for me, though I have to say it's repulsive! Note the order of the components in Fat_Pointer, which is the opposite to what I started with, and the size of the record on this 64-bit machine (I put the rep clause in to have make the order explicit, it works fine without). Also, I think you're stuck with the new.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with System;
procedure Fat is
type Index_Typ is mod 20;
type Data_Buffer_Typ is array (Index_Typ range <>) of Integer;
type Data_Buffer_Ptr is access all Data_Buffer_Typ;
Data_Buffer : aliased Data_Buffer_Typ (Index_Typ) := (others => 0);
type Result_Typ is (Ok, Overflow, Null_Pointer);
procedure Retrieve (Index : in Index_Typ;
Len : in Index_Typ;
Data_Ptr : out Data_Buffer_Ptr;
Result : out Result_Typ)
is
type Bound is (Lower, Upper);
type Bounds is array (Bound) of Index_Typ;
type Bounds_P is access Bounds;
type Fat_Pointer is record
The_Data : System.Address;
The_Bounds : Bounds_P;
end record;
for Fat_Pointer use record
The_Data at 0 range 0 .. 63;
The_Bounds at 8 range 0 .. 63;
end record;
function To_Data_Buffer_Ptr
is new Ada.Unchecked_Conversion (Fat_Pointer, Data_Buffer_Ptr);
Answer : constant Fat_Pointer
:= (The_Bounds => new Bounds'(Lower => Index,
Upper => Index + Len - 1),
The_Data => Data_Buffer (Index)'Address);
begin
Result := Ok;
Data_Ptr := To_Data_Buffer_Ptr (Answer);
end Retrieve;
Ptr : Data_Buffer_Ptr := null;
Result : Result_Typ;
begin
for J in Data_Buffer'Range loop
Data_Buffer (J) := Integer (J);
end loop;
Retrieve (2, 3, Ptr, Result);
for J in Ptr'Range loop
Put_Line (J'Img & " => " & Ptr (J)'Img);
end loop;
end Fat;

Array of variant records in Ada

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.

Resources