* 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