How to use a generic type? - ada

I'm working through the example here: https://www.adahome.com/rm95/rm9x-12-08.html
I've written my generic_stack.ads:
generic
type Item_Type is private;
size : Positive;
package Generic_Stack is
procedure push( item : in Item_Type );
function pop return Item_Type;
function is_Empty return Boolean;
STACK_EMPTY : exception;
STACK_FULL : exception;
end Generic_Stack;
And my generic_stack.adb:
package body Generic_Stack
is
type Stack_Table is array (Positive range <>) of Item_Type;
nodes : Stack_Table( 1..size );
index : Natural := 0;
procedure push( item : in Item_Type )
is
begin
if ( index < size )
then
index := index + 1;
nodes(index) := item;
else
raise STACK_FULL;
end if;
end push;
function pop()
return
Item_Type
is
item : Item_Type;
begin
if ( index > 0 )
then
item := nodes( index );
index := index - 1;
else
raise STACK_EMPTY;
end if;
return item;
end pop;
-- function is_Empty() removed for the sake of brevity
end Generic_Stack;
I don't really understand how to actually use the Generic_Stack.
With the simple generic_stack_test.adb code:
with Generic_Stack;
package Stack_Int_Type is new Generic_Stack( Item_Type => Integer, Size => 32 );
procedure Generic_Stack_Test
is
stack : Stack_Int_Type;
begin
stack.push( 3 );
end Generic_Stack_Test;
Gnat gives me errors on compilation:
# gnat make -gnat95 generic_stack_test.adb -o generic_stack_test
x86_64-linux-gnu-gcc-8 -c -gnat95 generic_stack_test.adb
generic_stack_test.adb:9:08: keyword "body" expected here [see file name]
generic_stack_test.adb:20:24: missing "end Stack_Int_Type;"
x86_64-linux-gnu-gnatmake-8: "generic_stack_test.adb" compilation error
Do I have to declare the Stack_Int_Type or suchlike? I don't understand how to use a declare inside a procedure. If I pass a Stack_Int_Type to another procedure, does it have to declare the type too?
Is it possible to simply declare Stack_Int_Type once in an .ads, and use it as a regular type? My book and web-pages kind of suggest it has to be declared every time, which sounds onerous.

Your test code is actually two library items:
with Generic_Stack;
package Stack_Int_Type is new Generic_Stack( Item_Type => Integer, Size => 32 );
declares a library package Stack_Int_Type, and
procedure Generic_Stack_Test
is
stack : Stack_Int_Type;
begin
stack.push( 3 );
end Generic_Stack_Test;
declares a library procedure which, as it stands, knows nothing about Stack_Int_Type.
We can fix that by adding the necessary with, but (compiling with -gnatl)
1. with Stack_Int_Type;
2. procedure Generic_Stack_Test
3. is
4. stack : Stack_Int_Type;
|
>>> subtype mark required in this context
>>> found "Stack_Int_Type" declared at stack_int_type.ads:2
5. begin
6. stack.push( 3 );
1 2
>>> invalid prefix in selected component "stack"
>>> prefixed call is only allowed for objects of a tagged type
7. end Generic_Stack_Test;
What’s happening here is that Generic_Stack doesn’t declare a type, so you can’t declare an instance of it at line 4; it’s a sort of singleton. (Amongst other things, that means it’s confusingly named: I’d’ve called it Integer_Stack. Never call a package _Type; _Types, maybe.)
Fixing that,
with Generic_Stack;
package Integer_Stack is new Generic_Stack( Item_Type => Integer, Size => 32 );
and
with Integer_Stack;
procedure Generic_Stack_Test
is
begin
Integer_Stack.push( 3 );
end Generic_Stack_Test;
You could have made Integer_Stack local:
with Generic_Stack;
procedure Generic_Stack_Test
is
package Integer_Stack
is new Generic_Stack( Item_Type => Integer, Size => 32 );
begin
Integer_Stack.push( 3 );
end Generic_Stack_Test;

Your Generic_Stack package never defines a stack data type.
The procedure Push and Pop are operations on a stack. You must have a type upon which to operate. There are two general categories of stacks; bounded stacks and unbounded stacks. You must decide which kind of stack you wish to create.
A discussion of both kinds of stacks implemented in Ada can be found at https://sworthodoxy.blogspot.com/2019/02/stack-abstract-data-type-using-ada.html
In the examples referenced in the URL above you will see how to create stack types that can be used multiple times. The example from Adahome is an old and problematic example. You have identified the biggest problem.

As explained elsewhere, no type is defined by your package specification. Otherwise, you would have type ... is somewhere after the package keyword. It's that simple.
But as explained elsewhere too, it's not dramatic. Your package instantiation will define exactly one stack, and not a type to be used in multiple places. In some cases, it's exactly what you need. So, your could call your instantiated package My_Stack, which actually articulates around an object (My_Stack.nodes, accessible only through Push & Pop).
You need to do the instantiation of Generic_Stack from within a unit (procedure, package, ...). Outside of it, it is the "outer space" only with with and use clauses needed to connect with other units.
with Generic_Stack;
procedure Generic_Stack_Test
is
package My_Stack is new Generic_Stack( Item_Type => Integer, Size => 32 );
begin
My_Stack.push( 3 );
end Generic_Stack_Test;

Related

How to get around forbidden discriminants defaults for tagged records in Ada?

I am learning Ada and I've hit a design problem. Excuse me as I'm not up with basic Ada mechanisms and idioms.
Let's say I want to represent an operation. Operators can be either plus or minus and operands can be either integers or strings.
Disclaimer: some things may not make much sense on a semantic level (minus on strings, operators without operands, ...) but it's all about representation.
For now I have the following incorrect code:
operand.ads:
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
-- This needs to be tagged
type Instance (Op_Type : Operand_Type := None) is tagged record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
-- Some methods on operands...
end Operand;
operation.ads:
with Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
main.adb:
with Operand;
with Operation;
procedure Main is
Op : Operation.Instance;
begin
Op.Left := (Op_Type => Operand.Int, Int_Value => 1);
Op.Right := (Op_Type => Operand.Int, Int_Value => 3);
end Main;
When I try to compile I get the following errors:
$ gnatmake main.adb
gcc -c main.adb
operand.ads:7:45: discriminants of nonlimited tagged type cannot have defaults
operation.ads:9:28: unconstrained subtype in component declaration
gnatmake: "main.adb" compilation error
I get why I can't use defaults on tagged type's discriminant but I don't really know how to get around this limitation.
Proposal 1:
Stop using variant records and use a record with one field for each operand. But I feel like this is just throwing away code elegance.
Proposal 2:
Remove defaults from Operand.Instance record and constrain Left and Right from Operation.Instance record. But I get a runtime error :
raised CONSTRAINT_ERROR : main.adb:7 discriminant check failed
As I cannot dynamically change discriminant value of a record's field.
Any help would be much appreciated !
Jim Rogers already discussed using inheritance. You can also use composition if you like by creating an internal non tagged type (which allows defaults), make the Operand.Instance type tagged private, have the private implementation use the internal non tagged version, and just add what operations you need to set and get the operands:
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
package Operand is
-- I want a None for unary operands or operators without operands
type Operand_Type is (Int, Str, None);
Invalid_Operand : exception;
-- This needs to be tagged
type Instance is tagged private;
function Int_Value(Value : Integer) return Instance;
function Str_Value(Value : String) return Instance;
function Null_Instance return Instance;
function Int_Value(Self : Instance) return Integer;
function Str_Value(Self : Instance) return String;
function Op_Type(Self : Instance) return Operand_Type;
-- Some methods on operands...
private
type Instance_Internal (Op_Type : Operand_Type := None) is record
case Op_Type is
when Int =>
Int_Value : Integer;
when Str =>
Str_Value : String (1 .. 10);
when None =>
null;
end case;
end record;
type Instance is tagged record
Impl : Instance_Internal;
end record;
function Int_Value(Value : Integer) return Instance is (Impl => (Int, Value));
function Str_Value(Value : String) return Instance is (Impl => (Str, Value));
function Null_Instance return Instance is (Impl => (Op_Type => None));
function Int_Value(Self : Instance) return Integer
is (if Self.Impl.Op_Type = Int then Self.Impl.Int_Value else raise Invalid_Operand);
function Str_Value(Self : Instance) return String
is (if Self.Impl.Op_Type = Str then Self.Impl.Str_Value else raise Invalid_Operand);
function Op_Type(Self : Instance) return Operand_Type
is (Self.Impl.Op_Type);
end Operand;
package Operation is
type Operation_Type is (Plus, Minus);
-- This also needs to be tagged
type Instance is tagged record
Left, Right : Operand.Instance;
end record;
-- Some methods on operations...
end Operation;
Op : Operation.Instance;
begin
Put_Line("Hello, world!");
Op.Left := Operand.Int_Value(1);
Op.Right := Operand.Int_Value(3);
Put_Line(Integer'Image(Op.Left.Int_Value));
Put_Line(Integer'Image(Op.Right.Int_Value));
end Hello;
You can break the Operand package into spec and body for better readability, this was just for example.

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;

How to save an Access type of a Discriminant record for later use

Issue:
How do I save an Access Pointer to a discriminant record for use later on in the program?
In main.adb (1) I demonstrate how I was able to get it to compile, but I get a runtime error:
raised PROGRAM_ERROR : main.adb:14 accessibility check failed
Note:
This is small example program based on a much larger/complex codebase.
Constraints:
i. The solution is required to be Ada95 Compatible.
ii. The solution must not change the package specification of Foo.ads as this is existing code that must be used as-is.
foo.ads
with Interfaces;
package Foo is
type Base_Class is abstract tagged limited private;
type Base_Class_Ref is access all Base_Class'Class;
for Base_Class_Ref'Storage_Size use 0;
Max_Count : constant := 6;
type Count_Type is new Interfaces.Unsigned_16 range 1 .. Max_Count;
type Foo_Class (Max : Count_Type) is new Base_Class with private;
type Foo_Class_Ref is access all Foo_Class;
for Foo_Class_Ref'Storage_Size use 0;
--
procedure Initialize (This_Ptr : Access Foo_Class);
--
function Get_Using_Pointer (This_Ptr : in Foo_Class_Ref) return Interfaces.Unsigned_16;
private
type Base_Class is abstract tagged limited null record;
type My_Data_Type is
record
X, Y, Z : Interfaces.Unsigned_16;
end record;
type My_Data_Array is
array (Count_Type range <>) of My_Data_Type;
type Foo_Class (Max : Count_Type) is new Base_Class with
record
Other_Data : Interfaces.Unsigned_16;
Data : My_Data_Array(1 .. Max);
end record;
end Foo;
foo.adb
package body Foo is
-- --------------------------------------------------------------------
procedure Initialize (This_Ptr : Access Foo_Class) is
begin
This_Ptr.Other_Data := 0;
This_Ptr.Data := (others => (0,0,0));
end Initialize;
-- --------------------------------------------------------------------
function Get_Using_Pointer (This_Ptr : in Foo_Class_Ref)
return Interfaces.Unsigned_16 is
begin
return This_Ptr.Other_Data;
end Get_Using_Pointer;
end Foo;
main.adb
-------------------------------------------------------------------------------
--
-- Issue:
-- How do I save an Access Pointer for later use (1) to a discriminent record?
--
-- Constraints:
-- i. The solution is required to be Ada95 Compatible.
-- ii. The solution must not change the package specification of Foo.ads
--
-------------------------------------------------------------------------------
--
with Interfaces;
with Foo;
procedure Main is
Foo_Count : constant := 3;
Foo_Obj : aliased Foo.Foo_Class (Max => Foo_Count);
procedure TEST (This_Ptr : access Foo.Foo_Class) is
-- (1) Save Pointer
-- **** This Line reports: ****
-- raised PROGRAM_ERROR : main.adb:14 accessibility check failed
Foo_Ptr : Foo.Foo_Class_Ref := This_Ptr.all'Access; -- This Compiles...
-- ^^^ I know that this is not correct.
-- But it was the only way I could find to get it to compile.
Data : Interfaces.Unsigned_16;
begin
-- (2) Get Data
Data := Foo.Get_Using_Pointer(This_Ptr => Foo_Ptr); -- This Compiles...
end;
begin
Foo.Initialize(This_Ptr => Foo_Obj'Access);
Test(This_Ptr => Foo_Obj'Access);
end Main;
Quick answer:
Foo_Ptr : Foo.Foo_Class_Ref := This_Ptr.all'Unchecked_Access;
Checked as far as I can with
lockheed:jerunh simon$ gnatmake main.adb -gnat95 -f
gcc -c -gnat95 main.adb
gcc -c -gnat95 foo.adb
gnatbind -x main.ali
gnatlink main.ali
lockheed:jerunh simon$ ./main
lockheed:jerunh simon$
In the line
Foo_Ptr : Foo.Foo_Class_Ref := This_Ptr.all'Access;
replace 'Access with 'Unchecked_Access.
PS. It could cause a dangling references if you destroy the object before Foo_Ptr gone.
The types Base_Class_Ref and Foo_Class_Ref are named access types and variables of this type can only refer to objects either on the heap or on package level, NOT objects on the stack. Since Storage_Size is set to zero it means the heap is out of the question.
package Main_App is
procedure Run;
end Main_App;
package body Main_App is
procedure TEST (This_Ptr : access Foo.Foo_Class) is
-- (1) Save Pointer
-- **** This Line reports: ****
-- raised PROGRAM_ERROR : main.adb:14 accessibility check failed
Foo_Ptr : Foo.Foo_Class_Ref := This_Ptr.all'Access; -- This Compiles...
-- ^^^ I know that this is not correct.
-- But it was the only way I could find to get it to compile.
Data : Interfaces.Unsigned_16;
begin
-- (2) Get Data
Data := Foo.Get_Using_Pointer(This_Ptr => Foo_Ptr); -- This Compiles...
end TEST;
Foo_Count : constant := 3;
Foo_Obj : aliased Foo.Foo_Class (Max => Foo_Count);
procedure Run is
begin
Foo.Initialize (This_Ptr => Foo_Obj'Access);
TEST (This_Ptr => Foo_Obj'Access);
end Run;
end Main_App;
with Main_App;
procedure Main is
begin
Main_App.Run;
end Main;
I hope this solution applicable to your use-case since it avoids usage of Unchecked_Access.
Ok what you're dealing with here is an anonymous access type, from the signature procedure TEST (This_Ptr : access Foo.Foo_Class). The error is telling you that this particular subprogram is in a deeper nesting than the thing it's pointing to: IOW, it could give you a dangling reference.
The proper solution, staying strictly in Ada95 would be to (A) put the TEST subprogram in the library unit [IIRC; 95 and 2005 are so similar they blur together]; or (B) put use a generic.
For a generic, IIRC, you can do this:
Generic
Object : Aliased Foo_Class'Class; -- Might not need 'Class.
with Function Operation(This_Ptr : in Foo_Class_Ref) return Interfaces.Unsigned_16;
Procedure Execute;
--...
Procedure Execute is
Result : Interfaces.Unsigned_16;
Begin
Result:= Operation( Object'Access );
End Execute;
----------------------------------------
O : Aliased Foo.Foo_Class(3);
Procedure TEST is new Foo.Execute( Operation => Foo.Get_Using_Pointer, Object => O );
This might require a little fiddling for your application, but if you put the generic inside Foo.ads/Foo.adb`, it should work. [IIRC] Aside from this, your best bet is to move your aliased object outside your main subprogram's declaration area, then it should work.

Dining Philosopher problem Ada- Implementing ID Dispenser

I have the following code, related to the dining philosopher problem. I am very new to Ada so am not sure about how to implement the Id_Dispenser package.
with Ada.Text_IO; use Ada.Text_IO;
with Id_Dispenser;
with Semaphores; use Semaphores;
procedure Philos is
No_of_Philos : constant Positive := 5; -- Number of philosophers
Meditation : constant Duration := 0.0;
type Table_Ix is mod No_of_Philos;
Forks : array (Table_Ix) of Binary_Semaphore (Initially_Available => True);
package Index_Dispenser is new Id_Dispenser (Element => Table_Ix);
use Index_Dispenser;
task type Philo;
task body Philo is
Philo_Nr : Table_Ix; -- Philisopher number
begin
Dispenser.Draw_Id (Id => Philo_Nr);
Put_Line ("Philosopher" & Table_Ix'Image (Philo_Nr) & " looks for forks.");
Forks (Philo_Nr).Wait; delay Meditation; Forks (Philo_Nr + 1).Wait;
Put_Line ("Philosopher" & Table_Ix'Image (Philo_Nr) & " eats.");
Forks (Philo_Nr).Signal; Forks (Philo_Nr + 1).Signal;
Put_Line ("Philosopher" & Table_Ix'Image (Philo_Nr) & " dropped forks.");
end Philo;
Table : array (Table_Ix) of Philo; pragma Unreferenced (Table);
begin
null;
end Philos;
I have implemented the following semaphore, which I think should be correct
package body semaphores is
protected body Binary_Semaphore is
entry Wait when Count > 0 is
begin
Count := Count - 1;
end Wait;
entry Release when Count < 1 is
begin
Count := Count + 1;
end Signal
end Binary_Semaphore;
end semaphores;
What does the Id_Dispenser need?
Looking at your code,
type Table_Ix is mod No_of_Philos;
...
package Index_Dispenser is new Id_Dispenser (Element => Table_Ix);
we can tell that Id_Dispenser is a generic package with a formal type named Element, and that the formal type is modular:
generic
type Element is mod <>;
package Id_Dispenser is
This
Philo_Nr : Table_Ix; -- Philisopher number
begin
Dispenser.Draw_Id (Id => Philo_Nr);
tells us that Id_Dispenser has some sort of component called Dispenser with a subprogram Draw_Id with an out parameter named Id which returns an Element.
Now, since this is a concurrent program, I'm going to guess that Dispenser is a protected object:
protected Dispenser is
procedure Draw_Id (Id : out Element);
private
...
end Dispenser;
The private part could simply be an array of Boolean indexed by Element,
Available : array (Element) of Boolean := (others => True);
but unfortunately you can't have an anonymous array as a component, so you need a proper type, giving
generic
type Element is mod <>;
package Id_Dispenser is
type Availability is array (Element) of Boolean;
protected Dispenser is
procedure Draw_Id (Id : out Element);
private
Available : Availability := (others => True);
end Dispenser;
end Id_Dispenser;
I'm not happy that the type Availability is visible, but the package now just needs implementing (!)
We could make Availability invisible by making Id_Dispenser.Dispenser a package, with Availability and the actual PO declared in the body. But that may be getting a little too purist for Ben’s context.
Firstly, you shouldn't really shorten identifiers, so you should have task type Philosophers... You can always use a renaming later on.
Shouldn't you model the forks and the philosophers? Each Philosopher as a task (hint array of task types).
Look at protected objects to model the forks.
Id_dispenser needs to implement a Draw_ID method.
Since the Dispenser variable is not declared here, it must presumably be declared in Id_dispenser. This hidden declaration is not very good style, as you can see it causes confusion; I would use a qualified name to make it obvious where it came from, as Index_Dispenser.Dispenser (which can then be renamed to reduce clutter in the rest of the code).
Id_dispenser may also need to provide an object factory method to initialise the Dispenser variable at its declaration.
Or, the intent may be that Dispenser will be the only one of its type, in which case you can treat Id_dispenser as a singleton package with Dispenser as the only object.

Mutually dependent type declarations and Ada.Containers

In an implementation of "boxed types" (for an interpreter) I originally had vectors in a child package and used System.Access_To_Address_Conversions to convert from System.Address to Vector_Ptr as needed in order to avoid seemingly insurmountable problems with cyclic dependencies. (At least, no use of limited with every did the trick for me.) It worked but seemed like a nasty hack. So I've decided to put the container types into the main package Types.Boxed. Now GNAT complains 'no declaration in visible part for incomplete type "Vector" defined at line 12'
Is there any way to fix this? Or should I return to my nasty hack?
Ada 2005 using GNAT 4.6 with flag -gnat05
with Interfaces; use Interfaces;
with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded;
with Ada.Containers.Vectors;
with Green_Tasks; use Green_Tasks;
package Types.Boxed is
type Type_T is (T_Null, T_Unsigned_64, T_String, T_Boolean,
T_Green_Task, T_Vector);
type String_Ptr is access all Unbounded_Wide_String;
type Vector;
type Vector_Ptr is access all Vector;
type Item (IType : Type_T := T_Null) is record
case IType is
when T_Null => null;
when T_Unsigned_64 => Value_Unsigned_64 : Unsigned_64;
when T_String => Value_String : String_Ptr;
when T_Boolean => Value_Boolean : Boolean;
when T_Green_Task => Value_Green_Task : Green_Task_Ptr;
when T_Vector => Value_Vector : Vector_Ptr;
end case;
end record;
procedure Free (Datum : in out Item);
procedure Box (Datum : out Item; Value : in Unsigned_64);
function Unbox (Datum : Item) return Unsigned_64;
procedure Box (Datum : out Item; Value : String_Ptr);
function Unbox (Datum : Item) return String_Ptr;
procedure Box (Datum : out Item; Value : in Boolean);
function Unbox (Datum : Item) return Boolean;
procedure Box (Datum : out Item; Value : in Green_Task_Ptr);
function Unbox (Datum : Item) return Green_Task_Ptr;
function Get_Boxed_Type (Datum : Item) return Type_T;
-- vectors
package Item_Vectors is new Ada.Containers.Vectors
( Index_Type => Natural,
Element_Type => Item
);
use Item_Vectors;
function Vector_New (Size_Hint : Positive) return Item;
function Unbox (Datum : Item) return Vector_Ptr;
procedure Vector_Free (V : in out Vector_Ptr);
function Vector_Copy (V : Vector_Ptr) return Item;
pragma Inline (Box);
pragma Inline (Unbox);
pragma Pure_Function (Unbox);
pragma Pure_Function (Get_Boxed_Type);
end Types.Boxed;
OK, I assume that you thought when you instantiated Item_Vectors and said use Item_Vectors, that the Vector type in Item_Vectors would be the completion of the incomplete Vector you wrote earlier.
It doesn't. When you say use P, it means that all the names defined in P are now directly visible, so if P declares a type T, you can say T instead of saying P.T. But the symbols still belong to P. They do not become "part of" the package that contains the use. Thus, for instance, use Item_Vectors; means that you can say Empty_Cursor instead of Item_Vectors.Empty_Cursor. But there will not be a Types.Boxed.Empty_Cursor. The name still belongs to Item_Vectors.
What this means is that when you have an incomplete Vector type in Types.Boxed, there needs to be a completion in Types.Boxed. The Vector type in Item_Vectors does not become the completion of the type, and the use doesn't help with that.
Unfortunately, Ada doesn't let you complete the type with a "type rename" or a subtype. The best option I can think of is
type Vector is new Item_Vectors.Vector with null record;
Note that this causes all the operations in Item_Vector to be inherited for Vector. So this might work for you. But there could be some unanticipated problems. But I can't think of a better solution.
EDIT: It looks like Simon has a good possible solution.
I played around with this and found that I could get it to compile by making Item_Vectors of element type Item_Ptr:
type Item (<>);
type Item_Ptr is access all Item;
package Item_Vectors is new Ada.Containers.Vectors
( Index_Type => Natural,
Element_Type => Item_Ptr
);
subtype Vector is Item_Vectors.Vector;
type Vector_Ptr is access all Vector;
type Item (IType : Type_T := T_Null) is record
case IType is
when T_Null => null;
when T_Unsigned_64 => Value_Unsigned_64 : Unsigned_64;
when T_String => Value_String : String_Ptr;
when T_Boolean => Value_Boolean : Boolean;
when T_Vector => Value_Vector : Vector_Ptr;
end case;
end record;
(I removed Green_Tasks, hopefully not relevant to your problem).
I was interested to see from ARM 3.10.1(3) that the complete declaration can only be postponed to the body if the incomplete declaration is in the private part.

Resources