1

Providing read-only access to a variable may (of course?) be achieved through abstraction. For example, I could make the variable an in mode parameter of a callable entity, or of a generic. The uses of the variable (through these constant views) would then be confined to the callable or generic instance.

This structure is not easy to add to an existing program, I'd think, since the program already has been structured; also, it is not an independent solution as it requires coupling between the “read-only-ness” and the structure.

Another option is to make the variable private and export a function that returns its value. However, I wanted direct exposure, e.g. of a volatile constant that still is a variable from a different point of view.

I came up with an overlay:

with Interfaces;

package Read_Only is
   subtype Pins is Interfaces.Unsigned_16;

   V : constant Pins with Volatile, Import;

private
   Backing : Pins with Volatile;
   for V'Address use Backing'Address;
   procedure Reset;
end Read_Only;

This shields V so that only the package body (and children) can modify its value, while clients of the package can read V. But “hiding” all this behind aspects and addresses makes me think: Is there some other, more obvious way?

Edit: Reminded by @flyx's comment, a reader of the public part of the package will see constant and may well think that V is physically constant—which it isn't, being volatile. All the more I'd like to have something that preserves both the object-like character of V and the fact that it can't be changed from outside Read_Only. V here isn't actually a constant object but its declaration says so. I guess I'd like to declare a constant view of a recognisably volatile object, or of some object at all, without incurring the contingencies of a function.

B98
  • 1,229
  • 12
  • 20
  • Wouldn't the compiler be allowed to do certain optimizations based on the fact that `V` is `constant`, which will break your code because it is not actually constant? I would also say that a function is the best way because it signals to both the user and the compiler that the value may change over time. – flyx Mar 01 '17 at 09:43
  • I don't think that a compiler can optimise that, if ever (what do you have in mind?), in the presence of **volatile** – although it might inline a function returning a copy or reference (if possible) of a volatile variable. But how could optimisation take away **constant**? That compiler will be severely broken, I'd say. Also, **volatile** prevents the package from being `Pure`, while in general an Ada function, more so in pure packages, is a feeble indicator of values actually changing over time, even ones of no argument. But that's part of the question or wish: “constant” ≠ “read-only”. – B98 Mar 01 '17 at 11:49
  • I agree that `Volatile` will most probably forbid constant propagation. It was just a thought, I don't have the LRM memorized. Nevertheless, my GNAT emits a *warning: constant overlays a variable* when I do this. – flyx Mar 01 '17 at 11:56

2 Answers2

3

My advice would be, use a simple function and a private variable:

with Interfaces;

package Read_Only is
   subtype Pins is Interfaces.Unsigned_16;

   function V return Pins;

private
   Backing : Pins;
   function V return Pins is (Backing);
end Read_Only;

Ada's calling conventions will ensure that the object is returned in the most efficient manner anyway.

Also, implementing it as an expression function will ensure inlining of the call. You can also use the Inline_Always aspect if you want direct exposure and no call in every situation, even when you compile without optimization:

function V return Pins with Inline_Always;

In that case, the call is always inlined to the variable accessed, so in terms of emitted code it is strictly equivalent to direct access.

EDIT: Sorry, I just saw that you did not want a function. Given the above, I fail to see why though. Could you give a more precise reason ?

raph.amiard
  • 2,755
  • 2
  • 20
  • 23
  • 2
    I don’t think expression functions have to be inlined. I can see that it might make it easier for the compiler if it chose to do so. `Inline_Always` is implementation-defined in GNAT. – Simon Wright Feb 24 '17 at 12:03
  • Possible reason include: to have the client control the number of reads of the volatile variable; to defer establishing an interface that hides the variable entirely – a function would be middle ground, but will incur a distinction of a volatile variable as possibly becoming by-reference if not a by-copy type as `V` likely is, so more to think about; … this need applies to inlining as @SimonWright observed, and may arguably be seen as obscuring, depending on who is then “prevented” from looking at `V` directly. – B98 Feb 24 '17 at 12:09
  • @SimonWright good point. Expesssion functions are guaranteed to be inlined when their body is visible. – raph.amiard Feb 24 '17 at 12:21
  • @B98 In that case I don't think that you have a better solution than the overlay, via the 'Address representation clause. So possibly your answer is in the question :) – raph.amiard Feb 24 '17 at 12:23
  • 2
    Where is this guarantee about inlining visible bodies? – Simon Wright Feb 25 '17 at 08:26
  • @SimonWright my bad ! It's not actually guaranteed, yet another GNAT specific thing that I thought was standard. – raph.amiard Mar 03 '17 at 14:06
1

Interesting question.

Providing read-only access to a variable may (of course?) be achieved through abstraction. For example, I could make the variable an in mode parameter of a callable entity, or of a generic. The uses of the variable (through these constant views) would then be confined to the callable or generic instance.

This structure is not easy to add to an existing program, I'd think, since the program already has been structured; also, it is not an independent solution as it requires coupling between the “read-only-ness” and the structure.

I'm not sure how difficult a generic would be, but if you're willing to reconsider the usage of generics I think you could do this:

Generic
    Type Alpha(<>) is private;
Package Constant_Access is
    Type    Beta  is access constant Alpha;
    Subtype Gamma is not null Beta;

    Generic
        Item_Access : in Gamma;
    Package Access_Accessor is
        Item : Constant Alpha renames Item_Access.all;
        Pragma Volatile( Item );
    End Access_Accessor;

End Constant_Access;

Then, when you need to provide a constant-only view, you use an instantiation of Access_Accessor on an instantiation of Constant_Access on your particular type -- then, immediately after that instantiation, rename the instantiation's Item to the name of the value you wish to hide/make constant.

Yes, this is a little bit awkward, but it does ensure that the properties you want are adhered to (Namely, being possibly volatile.) even in the face of heavy optimizing.


Edited:

With
System.Address_To_Access_Conversions;

Generic
    Type Alpha(<>) is private;
Package Constant_Access with Preelaborate is

    Package Conversions is new System.Address_To_Access_Conversions( Alpha );
    Use Conversions, System;

    -----------------------
    -- Type Declarations --
    -----------------------
    Type    Beta    is access constant Alpha;
    Subtype Gamma   is not null Beta;
    Type    Epsilon( G : Not Null Access Constant Alpha ) is null record
        with Implicit_Dereference => G, Volatile;
    -- NOTE: You could excise Beta and Gamma, they are included for flexibility.

    --------------------------
    -- Conversion Functions --
    --------------------------

    Function "+"( Right : Aliased Alpha ) return Gamma is
      ( Right'Access );
    Function "+"( Right : Gamma ) return Alpha is
      ( Right.All );
    Function "+"( Right : Gamma ) return Epsilon is
      ( G => Right.All'Access );
    Function "+"( Right : Epsilon ) return Gamma is
      ( Right.G );
    Function "+"( Right : Aliased Alpha ) return Epsilon is
      ( G => Right'Access );
    Function "+"( Right : Epsilon ) return Alpha is
      ( Right.G.All ); -- Not actually needed due to implicit dereferencing.
    Function "+"( Right : Not Null Access Constant Alpha ) return Address is
      ( Right.All'Address );
    Function "+"( Right : Address ) return Alpha is
      ( To_Pointer(Right).All );
    Function "+"( Right : Epsilon ) return Address is
      ( +Right.G );
    Function "+"( Right : Address ) return Epsilon is
      ( G => +To_Pointer(Right).All );    

    -----------------------
    -- Accessor Generics --
    -----------------------
    Generic
        Item_Access : in Gamma;
    Package Access_Accessor is
        Item : Epsilon( Item_Access )
          with Volatile;
    End Access_Accessor;

    Generic
        Item_Address : in System.Address;
    Package Address_Accessor is
        Item : Epsilon := +Item_Address
          with Volatile;
    End Address_Accessor;

End Constant_Access;
Shark8
  • 4,095
  • 1
  • 17
  • 31
  • I was thinking about access to constant, but so far, not about using generics in addition! So far, I still couldn't make it work. `Access_Accessor` needed a few tweaks, as **constant** cannot be part of a renaming (minor),and `Volatile` cannot be added to `Item` my compiler tells me. It also, interestingly, points at [RM C.6(13)](http://www.ada-auth.org/standards/aarm12_w_tc1/html/AA-C-6.html#p13). There's the [gist](https://gist.github.com/98B/74ce568df4028260a75a8b1bf05cf5ed) of my current attempts at using your solution. – B98 Mar 08 '17 at 09:52
  • @B98 - That's what I get for not running it through the compiler first. In any case, I've added an expanded/edited (and compilable) chunk that should offer you all the flexibility you need. [Well, you can't use it in *Pure* units.] – Shark8 Mar 08 '17 at 15:51