Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ada: objects with variable-sized array attribute

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?

like image 866
aluriak Avatar asked Dec 12 '22 00:12

aluriak


2 Answers

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.

like image 182
Simon Wright Avatar answered Dec 28 '22 08:12

Simon Wright


procedure Visite(v : World_Visitor_Type;
                     w : in out Worlds.World); -- ERROR: invalid prefix in selected component "Worlds"

This won't ever work -- this is because Worlds is a generic package, type-wise there's no such actual-thing as Worlds.World. (This is to say that the type of parameter w can never match an instantiation.)

One way to solve this is to make World_Visitors generic with Worlds as a parameter. Like so:

   generic
      X : Positive;
   package temp is
      subtype Index is Integer range 1..X;
   end temp;

   generic
      with package K is new temp(<>);
   package temp_dependant is
      -- Note that parameter I now CAN depend on temp, via formal parameter K.
      function J( I: K.Index ) return Boolean is (True);
   end temp_dependant;

Another way would be to move the visitor into the Worlds package. Yet another would be to make the visitor's package a child of Worlds.

like image 38
Shark8 Avatar answered Dec 28 '22 07:12

Shark8