* 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