Reputation:
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
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
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
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