I want to create a tagged type inside a package that describes a 2D discrete space, with a size determined in running time. (context : implementation of a game of life)
First way I found was genericity :
generic
Size : Natural;
package Worlds is
type World_Type is tagged private;
type World is access World_Type'Class;
subtype Coordinate is Positive range 1..Size;
private
type World_Array is array (Coordinate, Coordinate) of Boolean;
type World_Type is tagged record
Content : World_Array;
end record;
end Worlds;
But, when implementing a visitor for worlds, genericity become a big problem :
with Worlds;
package World_Visitors is
type World_Visitor_Type is tagged private;
type World_Visitor is access World_Visitor_Type'Class;
procedure Visite(v : World_Visitor_Type;
w : in out Worlds.World); -- ERROR: invalid prefix in selected component "Worlds"
private
type World_Visitor_Type is tagged null record;
end World_Visitors;
GNAT can't compile that, because Worlds is a generic package. Then, because I don't want to write a Visitor for each possible world size, I try the C++ way : declare the size as attribute in the tagged type.
package Worlds is
type World_Type is tagged private;
type World is access World_Type'Class;
subtype Coordinate is Positive range <>;
function Init(Size : Natural) return World; -- initialize Content attribute as an array of length (Size*Size)
private
type World_Array is array (Coordinate, Coordinate) of Boolean;
type World_Type is tagged record
Content : World_Array;
Size : Natural;
end record;
end Worlds;
And, expectedly, this doesn't work, because World_Array needs an explicit range for Coordinates. In fact, I have no idea how to create a running-time-choosen sized array in a tagged type. I got some ideas from here, here, here or here, but nothing seems to make sense in this case.
How does Ada implement objects with variable-sized array attribute?
The normal Ada way to address this problem is to use a discriminant (see ARM 3.7).
In your case, this would look something like
package Worlds is
type World_Type (Size : Natural) is tagged private;
type World is access World_Type’Class; -- ‘’ to fix SO colour bug
private
type World_Array is array (Positive range <>, Positive range <>) of Boolean;
type World_Type (Size : Natural) is tagged record
Content : World_Array (1 .. Size, 1 .. Size);
end record;
end Worlds;
in which World_Array
is an example of an unconstrained array type (ARM 3.6). You’d create a new world by code like
W : Worlds.World := new Worlds.World_Type (Size => 100);
Note that you can’t change the Size
after the object has been created.
I left out Coordinate
; and you may get away without Init
, especially if you provide an initializer for Content
:
Content : World_Array (1 .. Size, 1 .. Size) :=
(others => (others => False));
Edited 26.iii.15: Code was creating an array of size Size + 1
x Size + 1
.