comp.lang.ada
 help / color / mirror / Atom feed
* How to use read-only variables?
@ 2015-01-04  0:58 hreba
  2015-01-04  1:50 ` Hubert
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: hreba @ 2015-01-04  0:58 UTC (permalink / raw)


My Ada book says that 'access const' types serve for readonly
variables but I am not sure whether I grasped the idea.

I want a readonly variable 'n' as component of a record 'R'. The 
variable which is accessed by 'n', let's call it 'nv' must be hidden, 
otherwise the client could change it.

Consequently 'R' must be extensible, that is tagged. It makes no sense
to use the base class alone (without something for 'n' to point to),
so it must be abstract. As the pointer-target relation wouldn't be
conserved in a simple assignment, the record must be limited. This
leads to

    type R is abstract tagged limited record
       n:	access constant Integer;
    end record;

    type Rv is new R with private;

private

    type Rv is new R with record
       nv:	aliased Integer;
    end record;

The value of 'n' would be defined in an initialization procedure:

    procedure Init (rec: in out Rv) is
    begin
       rec.n:= rec.nv'Access;
    end Init;

This however gives me an error message:

    non-local pointer cannot point to local object

Now I am really confused:

  - Are readonly variables really handled this way, typically?
  - How can rec.nv be local and rec.n not?
  - How would you write the correct initialization procedure?

All this seems pretty complicated to somebody used to Oberon, where
all this would be simply written as

TYPE
    R* = RECORD
            n-: INTEGER;
         END;

-- 
Frank Hrebabetzky		+55 / 48 / 3235 1106
Florianopolis, Brazil


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04  0:58 How to use read-only variables? hreba
@ 2015-01-04  1:50 ` Hubert
  2015-01-04  2:04 ` Hubert
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Hubert @ 2015-01-04  1:50 UTC (permalink / raw)


Here is my guess, and I want to stress that I am by no means an Ada 
expert. So maybe it's completely wrong but here it goes anyway:

When you call that function with the IN OUT parameter, you probably make 
the mistake of thinking this is like a C++ reference, but I would think 
that the variable you use for the Access attribute is indeed a local 
one. Perhaps the compiler copies it when you call the function and then 
copies it again back when you leave the function, thus

rec.nv'Access;

would refer to a local object inside the function and the pointer would 
become invalid when you leave the function.

My guess, I would be glad if it was right :)


---
This email has been checked for viruses by Avast antivirus software.
http://www.avast.com



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04  0:58 How to use read-only variables? hreba
  2015-01-04  1:50 ` Hubert
@ 2015-01-04  2:04 ` Hubert
  2015-01-04  2:33 ` Brad Moore
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Hubert @ 2015-01-04  2:04 UTC (permalink / raw)


Oh I forgot to look at a possible solution, but I have the feeling 
you're overshooting your target here. Can you explain again what you 
need? I don't quite understand the problem.
You want a component in a record that points to another component and 
that other component is hidden from the user? That sounds like you want 
something to which the user application has no direct access but then 
want to provide access through a pointer?



---
This email has been checked for viruses by Avast antivirus software.
http://www.avast.com

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04  0:58 How to use read-only variables? hreba
  2015-01-04  1:50 ` Hubert
  2015-01-04  2:04 ` Hubert
@ 2015-01-04  2:33 ` Brad Moore
  2015-01-04  2:55 ` Shark8
  2015-01-04 12:56 ` hreba
  4 siblings, 0 replies; 9+ messages in thread
From: Brad Moore @ 2015-01-04  2:33 UTC (permalink / raw)


On 15-01-03 05:58 PM, hreba wrote:
> My Ada book says that 'access const' types serve for readonly
> variables but I am not sure whether I grasped the idea.

The idea is that whatever that the access const designates (points to), 
it cannot be modified via that access.

>
> I want a readonly variable 'n' as component of a record 'R'. The
> variable which is accessed by 'n', let's call it 'nv' must be hidden,
> otherwise the client could change it.

For what you are trying to accomplish, it seems an awkward roundabout 
way to do it. The troubles you are having with access types might 
suggest that there is a better way to do what you want to do.

If you only want a read only integer component of record 'R', consider 
making it a non-mutable discriminant.

eg.

type R (N : Integer) is null record;

Then any declaration of type R will have N specified and the client 
cannot change it. There is no need for it to be hidden.

A : R (N => 3);  -- The N value of A cannot be modified

If you really want it to be hidden, then you can declare the type with 
unknown discriminants, but then you need an initialization function to 
create objects of that type, because you cannot declare unconstrained 
objects.  Clients would not be able to refer to the N value directly, 
but you create provide a function that returns the N value as part of 
the abstraction.

eg,

package P is
    type R (<>) is private;
    function Create (N : Integer) return R;
    function Get_N (Item : R) return Integer;
private
    type R (N : Integer) is null record;
    function Create (N : Integer) return R is
      (N => N);
    function Get_N (Item : R) return Integer is
      (Item.N);
end P;

If you want it to be read only from the client perspective but 
modifiable locally, use a private type.

package P is
    type R is private;
    function Create (N : Integer) return R;
    function Get_N (Item : R) return Integer;
private
    type R2 is record
       N : Integer;
    end record;

    function Create (N : Integer) return R is
       (N => N);

    function Get_N (Item : R) return Integer is
      (Item.N);
end P;

>
> Consequently 'R' must be extensible, that is tagged.

No need for tagged types in the suggestions above.


It makes no sense
> to use the base class alone (without something for 'n' to point to),
> so it must be abstract. As the pointer-target relation wouldn't be
> conserved in a simple assignment, the record must be limited.

No need for abstract or limited in the above either, and no need for 
access types, or dealing with the error messages in the original post.

Brad


This
> leads to
>
>     type R is abstract tagged limited record
>        n:    access constant Integer;
>     end record;
>
>     type Rv is new R with private;
>
> private
>
>     type Rv is new R with record
>        nv:    aliased Integer;
>     end record;
>
> The value of 'n' would be defined in an initialization procedure:
>
>     procedure Init (rec: in out Rv) is
>     begin
>        rec.n:= rec.nv'Access;
>     end Init;
>
> This however gives me an error message:
>
>     non-local pointer cannot point to local object
>
> Now I am really confused:
>
>   - Are readonly variables really handled this way, typically?
>   - How can rec.nv be local and rec.n not?
>   - How would you write the correct initialization procedure?
>
> All this seems pretty complicated to somebody used to Oberon, where
> all this would be simply written as
>
> TYPE
>     R* = RECORD
>             n-: INTEGER;
>          END;
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04  0:58 How to use read-only variables? hreba
                   ` (2 preceding siblings ...)
  2015-01-04  2:33 ` Brad Moore
@ 2015-01-04  2:55 ` Shark8
  2015-01-04 12:56 ` hreba
  4 siblings, 0 replies; 9+ messages in thread
From: Shark8 @ 2015-01-04  2:55 UTC (permalink / raw)


On 03-Jan-15 17:58, hreba wrote:
>
>     type R is abstract tagged limited record
>        n:    access constant Integer;
>     end record;
>
>     type Rv is new R with private;
>
> private
>
>     type Rv is new R with record
>        nv:    aliased Integer;
>     end record;

Try it like this:

With
Ada.Text_IO.Text_Streams;

procedure Test is


    package tmp is

       type R is abstract tagged limited record
          n:    access constant Integer;
       end record;

       type Rv is new R with private;

       function Make( I : Integer ) return Rv;
       function Val( Item : Rv ) return Integer;

    private

       type Rv is new R with record
          -- The following referenced "this" as a particular
          -- instance for the default value by using the type-name
          -- (and 'access) as a self-reference. we have to convert
          -- the de-reference to the parent class, and then access
          -- the appropriate value.
          nv:    aliased Integer:= R(Rv'access.all).n.all;
       end record;

       -- Note that we let the default values take care of ensuring
       -- that the values are properly in-sync.
       function Make( I : Integer ) return Rv is
         ( n => new Integer'(I), others => <> );

       function Val( Item : Rv ) return Integer is
         ( Item.nv );

    end tmp;

    use Ada.Text_IO;
begin

    declare
       K : tmp.Rv:= tmp.make( 129 );
    begin
       Put_Line( "K.n:" & K.Val'Img );
    end;

    Put_Line( "Done." );
end Test;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04  0:58 How to use read-only variables? hreba
                   ` (3 preceding siblings ...)
  2015-01-04  2:55 ` Shark8
@ 2015-01-04 12:56 ` hreba
  2015-01-04 15:34   ` sbelmont700
  2015-01-04 20:45   ` Brad Moore
  4 siblings, 2 replies; 9+ messages in thread
From: hreba @ 2015-01-04 12:56 UTC (permalink / raw)


On 01/03/2015 10:58 PM, hreba wrote:
> My Ada book says that 'access const' types serve for readonly
> variables but I am not sure whether I grasped the idea.
>
> I want a readonly variable 'n' as component of a record 'R'. The
> variable which is accessed by 'n', let's call it 'nv' must be hidden,
> otherwise the client could change it.
>
> Consequently 'R' must be extensible, that is tagged. It makes no sense
> to use the base class alone (without something for 'n' to point to),
> so it must be abstract. As the pointer-target relation wouldn't be
> conserved in a simple assignment, the record must be limited. This
> leads to
>
>     type R is abstract tagged limited record
>        n:    access constant Integer;
>     end record;
>
>     type Rv is new R with private;
>
> private
>
>     type Rv is new R with record
>        nv:    aliased Integer;
>     end record;
>
> The value of 'n' would be defined in an initialization procedure:
>
>     procedure Init (rec: in out Rv) is
>     begin
>        rec.n:= rec.nv'Access;
>     end Init;
>
> This however gives me an error message:
>
>     non-local pointer cannot point to local object
>
> Now I am really confused:
>
>   - Are readonly variables really handled this way, typically?
>   - How can rec.nv be local and rec.n not?
>   - How would you write the correct initialization procedure?
>
> All this seems pretty complicated to somebody used to Oberon, where
> all this would be simply written as
>
> TYPE
>     R* = RECORD
>             n-: INTEGER;
>          END;
>

I am responding to my own post in order not to repeat myself in 
individual answers.

First of all: thanks for your help.

Second, I thought the meaning of "read-only" was clear, but perhaps it 
isn't. What I meant is what Brad expressed as "read only from the client 
perspective but modifiable locally".

Third, looking at the answer of Shark8 which solves the problem using 
"access const", I guess using a function returning the value of a hidden 
variable would be a much simpler solution. An example of a read-only 
variable would be the number of elements in a linked list. Within the 
defining package it is changed whenever the user adds or removes an 
element, you want the user to read it, but by no means you want to give 
him write access to it. I guess I understand now why 'Length' in 
Ada.Containers.Doubly_Linked_Lists is a function and no read-only variable.

Fourth, in what practical cases do you really use 'access const' for 
read-only variables; I thought my case a pretty typical one?

-- 
Frank Hrebabetzky		+55 / 48 / 3235 1106
Florianopolis, Brazil

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04 12:56 ` hreba
@ 2015-01-04 15:34   ` sbelmont700
  2015-01-04 20:45   ` Brad Moore
  1 sibling, 0 replies; 9+ messages in thread
From: sbelmont700 @ 2015-01-04 15:34 UTC (permalink / raw)


On Sunday, January 4, 2015 7:56:42 AM UTC-5, hreba wrote:
> 
> Fourth, in what practical cases do you really use 'access const' for 
> read-only variables; I thought my case a pretty typical one?
> 


A similar solution would be to have a function returning an 'access const' to the value, instead of the value itself, which replaces the return-by-reference mechanism (e.g. for when the value is not just an integer, but some other 'stateful' object).  Another common pattern you'll see is to return a null record with a single access discriminant to the value in order to control some of the lifetime considerations.

-sb


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04 12:56 ` hreba
  2015-01-04 15:34   ` sbelmont700
@ 2015-01-04 20:45   ` Brad Moore
  2015-01-04 20:48     ` Brad Moore
  1 sibling, 1 reply; 9+ messages in thread
From: Brad Moore @ 2015-01-04 20:45 UTC (permalink / raw)



> Fourth, in what practical cases do you really use 'access const' for
> read-only variables; I thought my case a pretty typical one?
>

I generally try to avoid using access types and access values if at all 
possible, especially in a visible part of a package, since that means 
the client has to deal with them. If I need to use them, I try to hide 
all use in the private part of the package, or in the body. I find it 
makes better abstractions when you do that, that are less error prone to 
use by the client.

I might use an access const internally in a package when there are 
multiple interconnected data objects, and if some of those comments are 
static or should not be modified within the abstraction.

eg...

package P is

    type Body_Names is
      (Sun,
       Mercury,
       Venus,
       Earth,
       Mars,
       Jupiter,
       Saturn,
       Uranus,
       Neptune,
       Pluto);

    subtype Planet_Names is Body_Names range Mercury .. Body_Names'Last;

    subtype Kilograms is Long_Long_Float;
    subtype Body_Mass is Kilograms;

    function Get_Mass (Body_Name : Body_Names) return Body_Mass;
    function Moon_Count (Planet_Name : Planet_Names) return Natural;
    function Moon_Mass (Planet_Name : Planet_Names;
                        Moon_Number : Natural) return Body_Mass;
end P;

package body P is

    type Star (Name_Length : Positive) is
       record
          Name : String (1 .. Name_Length);
          Mass : Body_Mass;
       end record;

    type Moon_Mass_Array is array (Positive range <>) of Body_Mass;

    type Planet (Name : Body_Names;
                 Moons : Natural) is
       record
          Moon_Masses : Moon_Mass_Array (1 .. Moons);
          Mass : Body_Mass;

       end record;

    type Planet_Array is array (Planet_Names) of access constant Planet;

    type Solar_System is
       record
          The_Sun : Star (Name_Length => Body_Names'Image (Sun)'Length);
          Planets : Planet_Array;
       end record;

    Planet_Mercury : aliased constant Planet :=
      (Name => Mercury,
       Moons => 0,
       Moon_Masses => <>,
       Mass => 3.3022E+23);

    Planet_Venus : aliased constant Planet :=
      (Name => Venus,
       Moons => 0,
       Moon_Masses => <>,
       Mass => 4.8676E+24);

    Planet_Earth : aliased constant Planet :=
      (Name => Earth,
       Moons => 1,
       Moon_Masses => (1 => 7.3477E+22),
       Mass => 5.97219E+24);

    ...

    Our_Solar_System : constant Solar_System :=
      (The_Sun => (Name_Length => Body_Names'Image (Sun)'Length,
                   Name => Body_Names'Image (Sun),
                   Mass => 1.989E+1030),
       Planets => (Mercury => Planet_Mercury'Access,
                   Venus => Planet_Venus'Access,
                   Earth => Planet_Earth'Access,
                   ...
                   ));

    function Get_Mass (Body_Name : Body_Names) return Body_Mass is
    begin
       return (if Body_Name = Sun then Our_Solar_System.The_Sun.Mass
               else Our_Solar_System.Planets (Body_Name).Mass);
    end Get_Mass;

    function Moon_Count (Planet_Name : Planet_Names) return Natural is
       (Our_Solar_System.Planets (Body_Name).Moons);

    function Moon_Mass (Planet_Name : Planet_Names;
                        Moon_Number : Natural) return Body_Mass is
      (Our_Solar_System.Planets (Planet_Name).Moon_Masses (Moon_Number));

end P;

Here all the data structures are hidden in the body. The client of 
package P does not even have to be aware of how the objects are 
interconnected, or that access values are being used.
I used access constant here because the data for the planets is static 
and cannot be modified. I used access types because the planet objects 
are variable in size and cannot be declared in an array, however an 
array of access values is possible.

Brad


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: How to use read-only variables?
  2015-01-04 20:45   ` Brad Moore
@ 2015-01-04 20:48     ` Brad Moore
  0 siblings, 0 replies; 9+ messages in thread
From: Brad Moore @ 2015-01-04 20:48 UTC (permalink / raw)


On 15-01-04 01:45 PM, Brad Moore wrote:
> and if some of those comments are static or

minor typo... should be;
    and if some of those *objects* are static or


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2015-01-04 20:48 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-04  0:58 How to use read-only variables? hreba
2015-01-04  1:50 ` Hubert
2015-01-04  2:04 ` Hubert
2015-01-04  2:33 ` Brad Moore
2015-01-04  2:55 ` Shark8
2015-01-04 12:56 ` hreba
2015-01-04 15:34   ` sbelmont700
2015-01-04 20:45   ` Brad Moore
2015-01-04 20:48     ` Brad Moore

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox