How to get a record member access pointer

I’m able to do a workaround for this, but I think it could be a more efficient, clean and elegant way to do it.
What I will love to do could look like this (It doesn’t compile):

package Containers is
type integer_Ref is access all Integer;
type container is tagged record
    member : aliased integer;
end record;
function Get_Member_Ref(S: container) return integer_Ref is (S.member'Access)
end Containers;

my_container : aliased container;
my_member_ref : integer_ref := my_container.Get_Member_Ref;

It will raise the error “access-to-variable-designates constant”

To overcome this, I need to create every member as a pointer for later in an initialisation stage, give for every member pointing to a separate “pool” package where the variables are aliased:

package pool is
   type MemberRef is access all integer;
   Obj_member : aliased integer := 5;
end pool;
----------------------
with pool; use pool;
package Containers is   
   type container is tagged record
      member : MemberRef;
   end record;
   function F_Get_memberRef (S: container) return MemberRef is (S.member);
   procedure init(S: in out Container);
end Containers;
----------------------
package body Containers is
   procedure init(S: in out Container) is
   begin
      S.member := pool.Obj_member'Access;
   end init;
end Containers;
----------------------
with containers; use containers;
with pool; use pool;
with ada.Text_IO; use ada.Text_IO;
----------------------
procedure Main is
   main       : container;
   memberRef : MemberRefType;   
begin
   main.Init;
   memberRef := main.F_Get_memberRef;
   Put_Line(memberRef.all'img);
end Main;

Problems with this way:

  • Every member (component) needs to be aliased in different packages. With enough complexity of members could be quite complex.
  • Needs a Initialization stage to assign every component.

I’ve tried to investigate AdaGems explaining Accessors (107, 123) with the hope that it could be usefull for what I’m looking for, but I had no luck…

Why Integer_Ref is not an access to constant?

type integer_Ref is access constant Integer;

In cases when you really wanted it mutable, it should be declared like this:

function Get_Member_Ref (S: not null access Container) return integer_Ref;

You can also pass S in in-out mode now, but I would not recommend that. It was an error to allow this in Ada.

And finally you can use the Rosen’s trick to circumvent the checks. It is useful in rare cases:

type Container;
type Container_Ptr is access Container'Class;
type Container is tagged record
   Self   : Container_Ptr := Container'Access;
   Member : aliased integer;
end record;
function Get_Member_Ref (S: Container) return integer_Ref is
begin
   return S.Self.Member'Unchecked_Access;
end Get_Member;
1 Like

Thank you Dmitry for your help.

Why Integer_Ref is not an access to constant?

Using access to constant, the error raised was “non-local pointer cannot point to local objects”. For this case, I only was able to make it work using a function returning S.member’Unchecked Access. (I think this is related with the Rosen’s trick that I will try to avoid at first instance)

function Get_Member_Ref (S: not null access Container) return integer_Ref;

I was not able to run it. It raises a PROGRAM_ERROR : containers.ads:8 accessibility check failed.
This was the code:

package Containers is
   
type integer_Ref is access constant Integer;
type container is tagged record
    member : aliased integer := 5;
end record;
   --function Get_Member_Ref_unchecked(S: container) return integer_Ref is (S.member'Unchecked_Access);
   function Get_Member_Ref (S: not null access Container) return integer_Ref is (S.member'Access);
end Containers;
-------------------------
with containers; use containers;
procedure Main is
   main       : aliased container;
   memberRef   : integer_Ref;
begin
   memberRef := main.Get_Member_Ref;
end Main;

You will always get accessibility check error. Use an access type to the container if you want 'Access rather than 'Unchecked_Access:

   type Integer_Ref is access constant Integer;
   type Container is tagged record
      Member : aliased integer;
   end record;
   function Get_Member_Ref (S: not null access Container) return Integer_Ref;

   function Get_Member_Ref (S: not null access Container) return Integer_Ref is
   begin
      return S.Member'Access;
   end Get_Member_Ref;
1 Like

@dmitry-kazakov It Works!. Thank you very much. I appreciate your help.

Couldn’t this be shortened using Ada’s expression syntax? So it would be:

type Integer_Ref is access constant Integer;
type Container is tagged record
   Member : aliased integer;
end record;
function Get_Member_Ref (S: not null access Container) return Integer_Ref is (S.Member'Access);

That avoids duplication and is allowed by GNAT in the spec :slight_smile:

Best regards,
Fer

Function definition inside declarative region is all against Ada philosophy of separation interface and implementation.