Question

General question: Is it possible to override fields of records?

As a simple example, I want to have a Long_Integer field, but the outside world should only be able to get/set it as Integer.

package A is
   type B is record // edit: should be tagged to be able to use my intention below
      c : Long_Integer;
   end record;
end A;

X : B;

I could fake my intention by using following function/procedure:

function d (Object : B) return Integer is (Integer (Object.c));
procedure d (Object : B; Value : Integer) is
begin
   Object.c := Long_Integer (Value);
end d;

So, I could use X.d to get the value and X.d () to set the value.

But, is it possible to override the field "c" with the given function and procedure and more interesting, can I override it so, that I could use X.c := Integer (3) to set the value of the field?


Edit:

test.adb

with Ada.Text_IO;
with A;

procedure Test is
   use A;

   X : C;
begin
   X.d (123);
   Ada.Text_IO.Put_Line("Value is " & X.d'Img);
end Test;

a.adb

package body A is

   function d (Object : C) return Integer is (Integer (Object.d));
   procedure d (Object : in out C; Value : Integer) is
   begin
      Object.d := Long_Integer (Value);
   end d;

end A;

a.ads

package A is

   type B is tagged private;
   type C is tagged private; -- tagged missed
   type C_Access is access all C; <-- is this line correct?

   function d (Object : C) return Integer;
   procedure d (Object : in out C; Value : Integer);

private

   type B is tagged null record;

   type C is new B with record
      d : Long_Integer;
   end record;

end A;

Compiler says: test.adb:9:04: invalid prefix in selected component "X" test.adb:10:39: invalid prefix in selected component "X"

Do you see, why?

Was it helpful?

Solution

I may be misunderstanding what you're asking but as I understand it you want the external view of the record to look like one thing, while the internal details are quite different.

Go a little further and hide the internal representation altogether.

package A is
   type B is tagged private;

   function c (Object : B) return Integer;
   procedure c (Object : B; Value : Integer);
   -- set_c is probably a better name!

private
-- the "private" part allows other code to allocate B 
-- without seeing the implementation (package body)

   type B is tagged record     
      c : Long_Integer;
   end record;

end A;

The package body is easy to write, not shown here : it contains the implementations of the accessors, more or less as you wrote them.

Then you can use the package:

with Ada.Text_IO;
with A;     -- assuming A is a separate pair of files, a.ads, a.adb.
            -- Unnecessary if A is a package in the current file.
use A;

procedure test is
   X : B;
begin
   X.c(123);  -- set value. 
   put_line("Value is " & integer'image(X.c));
end test;

NB this dot notation X.c is Ada-2005/2012 syntax. Ada-95 requires c(X) ... which is still valid and means the same thing.

Each package is also a namespace, so instead of use A; you could explicitly write

procedure test is
   X : A.B;
begin
   X.c(123);  -- set value
   put_line("Value is " & integer'image(X.c));
end test;

EDIT : actually trying your worked example instead of just answering(!) :

(1) My mistake : I should have pointed out the object.prefix notation for subprograms applies to tagged types; adding "tagged" to C's declaration in package spec A resolves this.

type C is tagged private;

(2) function d cannot just rename a type conversion; wrap it as

   function d (Object : C) return Integer is 
   begin
      return Integer (Object.d);
   end d;

and:

gnatmake test
gnatlink: warning: executable name "test" may conflict with shell command
./test
Value is 123

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top