user5069935
user5069935

Reputation:

Passing array of aliased records to a procedure

In Ada 2012, I want to have a linked list inside a declared array, rather than allocated. I want to have the linking pointers set by a procedure.

I have simplified my program to the following that demonstrates the principle I want to use but I cannot get this to compile in Gnat 4.9.2 (Debian Jessie) running on Raspi...

procedure Arr is
   type Cell;
   type Cell_Ptr is access all Cell;

   type Cell is
      record
         Number : Integer := 0;
         Next : Cell_Ptr := null;
      end record;

   type Chain is array (1 .. 100) of aliased Cell;

   procedure Make_Links (Ch : in out Chain);

   procedure Make_Links (Ch : in out Chain) is
   begin
      for I in Ch'First .. Ch'Last - 1 loop
         Ch (I).Next := Ch (I + 1)'Access;                 -- ERROR HERE
      end loop;
   end Make_Links;

   My_Chain : Chain;
begin
   Make_Links (My_Chain);
end Arr;

I get this compiler error: "non-local pointer cannot point to local object" at the line indicated above.

I know I'm trying to do something a bit odd but I plan on having a few other functions that perform the linking in different ways (backwards, or randomly etc) based on which procedure I pass this array of cells to.

How do I fix this code so that it compiles? Can't quite get my head around this one (I'm still a novice but enjoying the learning process).

Upvotes: 0

Views: 221

Answers (3)

user5069935
user5069935

Reputation:

I figured it out in the end. The following code is a modified version of the one in the OP. It does what I originally wanted without doing anything unpleasant...

with Ada.Integer_Text_IO, Ada.Text_IO;
use Ada.Integer_Text_IO, Ada.Text_IO;

procedure Arr is
   type Cell;
   type Cell_Ptr is access all Cell;

   type Cell is
      record
         Number : Integer := 0;
         Next : Cell_Ptr := null;
      end record;

   type Chain is array (1 .. 100) of aliased Cell;
   type Chain_Ptr is access all Chain;

   procedure Make_Links (CP : in out Chain_Ptr);

   procedure Make_Links (CP : in out Chain_Ptr) is
   begin
      for I in CP'First .. CP'Last - 1 loop
         CP.all (I).Next := CP.all (I + 1)'Access;
      end loop;
   end Make_Links;

   My_Chain : aliased Chain;
   My_CP : Chain_Ptr := null;
   My_C : Cell_Ptr := null;
begin
   My_CP := My_Chain'Access;
   Make_Links (My_CP);

   --  verify that the code works by writing values into the array
   for I in My_Chain'Range loop
      My_Chain (I).Number := 1000 * I;
   end loop;

   --  and read them back out using the pointer links
   My_C := My_Chain (My_Chain'First)'Access;
   while My_C /= null loop
      Put (My_C.Number);
      Put_Line ("");
      My_C := My_C.Next;
   end loop;

end Arr;

Instead of passing the array directly, I passed a pointer to the array instead, which Gnat seems happy with. I think what I was trying to do before was being scuppered by the "pass by copy" rules for procedure parameters.

Upvotes: 0

Simon Wright
Simon Wright

Reputation: 25501

Instead of using ’Access, use ’Unrestricted_Access. This is one of GNAT’s “implementation-defined” attributes:

The Unrestricted Access attribute is similar to Access except that all accessibility and aliased view checks are omitted. This is a user-beware attribute.

Upvotes: 0

user571138
user571138

Reputation:

As you are not actually allocating & freeing memory, in dont see the need for pointers. I would achieve the same functionality by doing something like this:

procedure Arr is

   type Cell_Index is new Integer range 0 .. 100;
   subtype Valid_Cell_Index is Cell_Index range 1 .. Cell_Index'Last;

   type Cell is
      record
         Number : Integer := 0;
         Next   : Cell_Index := 0;
      end record;

   type Chain is array (Valid_Cell_Index) of Cell;

   procedure Make_Links (Ch : in out Chain);

   procedure Make_Links (Ch : in out Chain) is
   begin
      for I in Valid_Cell_Index'First .. Valid_Cell_Index'Last - 1 loop
         Ch (I).Next := I+1;
      end loop;
   end Make_Links;

   My_Chain : Chain;
begin
   Make_Links (My_Chain);

end Arr;

This way you are still using Next as an index into the same array, and can pre-load your array with whatever linking pattern you want.

Upvotes: 3

Related Questions