Search code examples
arraysadagnat

How to implement Unchecked_Access


I am trying, again, to design a 2D array that expands automagically.

Rectangular.ads

generic
    type Value_Type is private;

package Rectangular is
    
    function Get ( Row, Col : Integer) return Value_Type;
    procedure Set ( Row, Col : Integer; Value : Value_Type);
    
private
    type Matrix is array (Integer range <>, Integer range <>) of aliased Value_Type;
    
    Item : access Matrix;
    
end Rectangular;

Rectangular.adb

package body Rectangular is

    function Create (Rowmin, Rowmax, Colmin, Colmax : Integer) return access Matrix is
    begin
        return Answer :  constant access Matrix :=
          new Matrix (Rowmin .. Rowmax, Colmin .. Colmax)
        do
            null; -- maybe something later...
        end return;
    end Create;
    
    procedure Adjust_Bounds (Row, Col : Integer) is
        
        Rowmin, Rowmax, Colmin, Colmax : Integer;
        Newitem :  access Matrix;
        
    begin
        
        if Row >= Item'First (1) and Row <= Item'Last (1) and
          Col >= Item'First (2) and Col <= Item'Last (2) then
            return;
        end if;

        -- Matrix needs expanding, establish new bounds
        Rowmin := Integer'Min (Item'First (1), Row);
        Rowmax := Integer'Min (Item'Last (1), Row);
        Colmin := Integer'Min (Item'First (2), Col);
        Colmax := Integer'Min (Item'Last (2), Col);
    
        Newitem := Create (Rowmin, Rowmax, Colmin, Colmax);
        
        -- Copy old to new
        for R in Item'Range (1) loop
            for C in Item'Range (2) loop
                Newitem (R, C) := Item (R, C);
            end loop;
        end loop;
        
        -- How to free Item here?
        Item := Newitem;
        
    end Adjust_Bounds;
    
    function Get (Row, Col : Integer) return Value_Type is
        Result : Value_Type;
    begin
        Adjust_Bounds (Row, Col);
        Result := Item (Row, Col);
        return Result;
    end Get;
    
    procedure Set ( Row, Col : Integer; Value : Value_Type) is
    begin
        Adjust_Bounds (Row, Col);
        Item (Row, Col) := Value;
    end Set;
    
begin
    Item := Create (0, 0, 0, 0);

end Rectangular;

main.adb

with Ada.Text_IO; use Ada.Text_IO;
with Rectangular;
procedure Main is
begin
    declare
        package Rect is new Rectangular (Value_Type => Integer);
        X : Integer;
    begin
        -- Only 0,0 exists initially
        Rect.Set (0, 0, 2);
        X := Rect.Get (0, 0);
        Put_Line (X'Image);
        
        -- Make the matrix expand
        Rect.Set (1, 1, 42);
        X := Rect.Get (1, 1);
        Put_Line (X'Image);
    end;
end Main;

This compiles, but with

   6:17 warning: "Program_Error" will be raised at run time
   6:17 warning: accessibility check failure
   6:17 warning: in instantiation at rectangular.adb:29

and I of course get "raised PROGRAM_ERROR : rectangular.adb:59 accessibility check failed" when I try and run it.

I don't understand why, as 'Rect' is not clearly accessible outside the scope of the block;

  1. Should I use Unchecked_Access to avoid this behaviour? If so, what would it look like?
  2. If not, what is the correct idiom?
  3. How should I free 'Item' in Rectangular.adb?

I've been trying to get this to work for several days with no success, help with working code examples would be greatly appreciated.


Solution

  • Add after type Matrix

    type Matrix_P is access Matrix;
    

    (use your own convention for naming access types).

    Then, globally replace access Matrix by Matrix_P.

    Then, in Adjust_Bounds, you appear to need to replace

          Rowmax := Integer'Min (Item'Last (1), Row);
    

    by

          Rowmax := Integer'Max (Item'Last (1), Row);
    

    and likewise for Colmax.