Say I have a type:
type A;
type XA is access constant A;
type A is
record
Member : Natural := 1;
Neighbor : XA;
end record;
I'm confused about the access constant part.
If I instantiate a instance of XA that points to a instance of A, what can I change while only holding the reference to the XA "instance" ?
Can I change the member of the object that XA points to ? I'd say no, but what about the Neighbor of the A in the XA object ?
Can someone explain the use of access constant to me ?
Here's a small example showing what it does:
procedure Access_Constant is
type XA is access constant Integer;
A : aliased Integer;
X : XA;
begin
X := A'Access;
X.all := 4;
end Access_Constant;
When you attempt to compile it, the assignment to X works fine (X is a variable), while the assignment to X.all is forbidden (as X.all is a constant - per the definition of XA).
Although XA is declared to point to a constant Integer, even a variable is acceptable, but you can only treat a dereference of an XA entity as a constant Integer, even if the object is a variable.
Related
I am going through the learn.adacore.com tutorial and have hit upon a problem that I am unsure of.
Specifically I understand that Ada is designed to trap attempts to overflow a variable with a specified range definition.
In the case below, the first attempt to do this causes a compiler 'range check failure' which is expected. However the following line doesn't trap it and I am not sure why:
with Ada.Text_IO; use Ada.Text_IO;
procedure Custom_Floating_Types is
type T_Norm is new float range -1.0 .. 1.0;
D : T_Norm := 1.0;
begin
Put_Line("The value of D =" & T_Norm'Image(D));
-- D := D + 1.0; -- This causes a range check failure at run time = completely expected.
Put_Line("The value of D =" & T_Norm'Image(D + 1.0)); -- This doesn't?
end Custom_Floating_Types;
You have a couple of pretty good answers, but I'm going to add another because it's clear that you expect the expression D + 1.0 to raise an exception, and the answers you have don't explain why it doesn't.
A type declaration like
type T_Norm is new float range -1.0 .. 1.0;
is roughly equivalent to
type T_Norm'Base is new Float;
subtype T_Norm is T_Norm'Base range -1.0 .. 1.0;
The type (called the "base type") isn't given a name, though it can often be referenced with the 'Base attribute. The name that is given is to a subtype, called the "first-named subtype".
This distinction is important and is often not given enough attention. As explained by egilhh, T_Norm'Image is defined in terms of T_Norm'Base. This is also true of the arithmetic operators. For example, "+" is defined as
function "+" (Left : in T_Norm'Base; Right : in T_Norm'Base) return T_Norm'Base;
2.0 is clearly in the range of T_Norm'Base, so evaluating D + 1.0 doesn't violate any constraints, nor does passing it to T_Norm'Image. However, when you try to assign the resulting value to D, which has subtype T_Norm, a check is performed that the value is in the range of the subtype, and an exception is raised because the check fails.
This distinction is used in other places to make the language work reasonably. For example, a constrained array type
type A is array (1 .. 10) of C;
is roughly equivalent to
type A'Base is array (Integer range <>) of C;
subtype A is A'Base (1 .. 10);
If you do
V : A;
... V (2 .. 4)
you might expect problems because the slice doesn't have the bounds of A. But it works because the slice doesn't have subtype A but rather the anonymous subtype A'Base (2 ..4).
The definition of 'Image says:
For every scalar subtype S:
S'Image denotes a function with the following specification:
function S'Image(Arg : S'Base)
return String
As you can see, it takes a parameter of the base (unconstrained) type
T_Norm'Image(D + 1.0) neither assigns nor reads an out-of-range value. It asks for the 'Image attribute (string representation) of (D + 1.0), which is the same as asking for (1.0 + 1.0).
I can see two ways the confusion might arise. First, the name "attribute" could be misinterpreted to suggest that 'Image involves something intrinsic to D. It doesn't. The 'Image attribute is just a function, so D is just part of the expression that defines the value of the parameter (in your example = 2.0).
Second, the 'Image attribute comes from Float. Thus, any Float can be its parameter.
You can create a function that accepts only parameters of T_Norm'Range and make that function an attribute of T_Norm. See Ada Reference Manual 4.10.
Because you don't store a value greater than range in D.
I have some methods in a package that operate on an access constant tot a tagged record; in order to call these functions, I must specify the package name. I would much rather just put the variable name [dot] function name, but this gives the error: no selector "foo" for type "Color". Why is that?
Here's a minimal reproducer:
procedure Main is
type Color is tagged
record
Hue : Integer;
Saturation : Integer;
Value : Integer;
end record;
type AccessColor is access constant Color;
procedure foo (C : in AccessColor) is
begin
null;
end foo;
AccessS : AccessColor;
begin
foo (AccessS);
--AccessS.foo; -- does not work. Why?
end Main;
Note that in my real code, it is inconvenient to specify the function fully, because unlike in the example above, foo is defined somewhere in a seperate package:
Some.Package.Name.Depp.foo(AccessS);
Even though AccessS already specifies where to find the function, so I should just be able to do:
AccessS.foo;
The problem is that foo isn’t actually a primitive operation of Color (in this reproducer).
ARM 3.3.2(6) says that the primitive subprograms of a specific type are
For a specific type declared immediately within a package_specification, any subprograms (in addition to the enumeration literals) that are explicitly declared immediately within the same package_specification and that operate on the type
This (apologies for reformatting, casing adjustment) compiles fine.
procedure Main is
package Pak is
type Color is tagged
record
Hue : Integer;
Saturation : Integer;
Value : Integer;
end record;
procedure Foo (C : in Color) is null;
type AccessColor is access constant Color;
end Pak;
Col : aliased Pak.Color;
AccessS : Pak.AccessColor := Col'Access;
begin
AccessS.Foo;
end Main;
I declared Foo as taking in Color; you could equally declare it to take access constant Color if you need to, because (ARM 4.1.3(9.2))
The first formal parameter of the subprogram shall be of type T, or a class-wide type that covers T, or an access parameter designating one of these types
This doesn't work because as you define it, foo is not a primitive operation of the tagged type Color. Prefix notation can only be used on primitive operations of tagged types.
The solution is to make foo a primitive operation of Color like this:
procedure foo (C : access constant Color) is
begin
null;
end foo;
If you use a named access type, foo will instead be a primitive operation of that type, and since that type is not a tagged type, prefix notation doesn't work.
Creating access type of array of integer gives an error. Below is my code, please help me to fix it.
procedure arry_point is
type arr is array(1..5) of integer;
obj:aliased arr;
type my_access1 is access all arr;
var1:my_access1:=obj'Access;-- this is good
ii: aliased array(1..5)of integer
type my_access is access all ii; --this is bad but how can i create access type for ii ?
var:my_access:=ii'access; ---?
begin
null;
end arry_point;
type My_Access is access ... what? The answer is, it has to be a type name (strictly a subtype_indication, see ARM 3.10(3).
When you say ii: aliased array(1..5) of integer you’re creating an array of an anonymous type; this means you can’t supply the type name to complete the access type definition.
You could imagine a language (C++?) in which you could say
type My_Access is access all Type_Of (II);
or, perhaps,
type My_Access is access all II'Type;
but neither of those is possible in Ada. I suspect the reason is that there wouldn’t be any point, because in Ada types are not equivalent even if they have the same structure:
1. procedure SG is
2. A : array (1 .. 5) of Integer := (others => 0);
3. B : array (1 .. 5) of Integer;
4. begin
5. B := A;
|
>>> expected type of B declared at line 3
>>> found type of A declared at line 2
6. end SG;
Difference between out mode and in out mode? As far i have collected the info is
the main difference i know is that in both out and in out mode the actual parameter is expected to be altered even writing and reading is also possible then what is main difference please help me to understand?
The best explanation of the difference is in RM 6.4.1(11-15).
Semantically, there is a difference only for parameters that are passed by copy. If you call a subprogram that has an in out parameter, the actual parameter is expected to have a legitimate value before the call. On entry, the subprogram makes a copy of the variable and checks to make sure it satisfies the constraints.
For an out parameter, the actual parameter does not need to have a value going in; it could be uninitialized garbage. The intent is that the subprogram will not use the value of this parameter until it has been set, by the subprogram (it can be set either by assignment or by being passed as an out parameter to some other subprogram, or to a Default_Value, or perhaps by other means). This is not enforced, however.
This can cause different behavior in some cases. For example:
subtype Int10 is Integer range 1 .. 10;
procedure Proc (Param : in out Int10) is
begin
Param := 5;
end Proc;
Y : Integer := 100;
...
Proc (Y);
Since Param is an in out parameter, the constraints are checked on entry. Thus, the call to Proc(Y) raises a Constraint_Error. However:
subtype Int10 is Integer range 1 .. 10;
procedure Proc (Param : out Int10) is
begin
Param := 5;
end Proc;
Y : Integer := 100;
...
Proc (Y);
In this case, no Constraint_Error is raised. In fact, Y does not need to be initialized at all, because the expectation is that the procedure will not use the input value. According to the RM, the value is not even copied, for certain classes of parameter types. So in this case:
Save_Param : Integer;
procedure Proc (Param : out Int10) is
begin
Save_Param := Param; -- legal but not recommended
Param := 5;
end Proc;
Y : Integer := 3;
...
Proc (Y);
Save_Param will probably be set to some garbage value, not 3. (In Ada 2012, there is a Default_Value aspect that can be applied to subtypes; in a case like that, an out parameter would be set to this default value instead of uninitialized garbage, while an in out parameter would still get the value from the actual parameter.)
For parameters that are passed by reference, there really is no difference in behavior.
The rules in Ada 83 were different. A subprogram with an in out parameter could both read and write that parameter, while a subprogram with an out parameter was allowed to assign to the parameter but could not do anything that would read that parameter's value (barring some cases where there was no way around reading discriminants). Thus:
procedure Proc (Param : out Int10) is
begin
Param := 5;
Save_Param := Param; -- illegal in Ada 83, legal in Ada 95 and later versions
end Proc;
In other words, an out parameter really was output-only. The rule was relaxed in Ada 95, though, as programmers found it too restrictive.
Hello Im new to ada and I am trying to create some kind of unconstrained array and I can't figure out how to do it in ada.
package data_puzzle is
type rotation is private;
type map(x_l,y_l,z_l : Natural) is private;
type valid_rotations is private;
private
type rotation is array(1..3) of Natural;
type map(x_l,y_l,z_l : Natural) is record
struct : Structure(x_l,y_l,z_l);
rot : rotation;
end record;
type valid_rotations is array(1..24) of map; --how can I make this row work?
end data_puzzle;
Structure looks like this
type structure(x_l,y_l,z_l : Natural) is record
structure : xyz(1..x_l,1..y_l,1..z_l);
X : Natural := x_l;
Y : Natural := y_l;
Z : Natural := z_l;
end record;
Basically I have a map with rotations and data. Then I want to store all the different rotations in a list of size 24. The only solution I have right now is to initiate
type valid_rotations is array(1..24) of map(x,y,z) then it works. But I do not want to initiate it like that since I do not know what the size will be at that moment.
Cheers!
Ok, the problem is that the type map can be of varying size -- the compiler cannot therefore simply set aside the proper amount of memory w/o further information -- so the solution is to create some sort of indirection which can be the element of an array: we have this type since Ada 83 but with Ada 2005 and on we can further constrain access types from being null.
-- I don't know what this is supposed to be; I'm assuming an array.
type xyz is Array(Positive Range <>, Positive Range <>, Positive Range <>)
of Integer;
type structure(x_l,y_l,z_l : Natural) is record
structure : xyz(1..x_l,1..y_l,1..z_l);
X : Natural := x_l;
Y : Natural := y_l;
Z : Natural := z_l;
end record;
package data_puzzle is
type rotation is private;
type map(x_l,y_l,z_l : Natural) is private;
type valid_rotations is private;
type map_handle is private;
private
type rotation is array(1..3) of Natural;
type map(x_l,y_l,z_l : Natural) is record
struct : Structure(x_l,y_l,z_l);
rot : rotation;
end record;
-- Because the size of the record "map" can change
-- depending on its discriminants, we cannot simply
-- make some array -- we can however have an array
-- of accesses (since we know their sizes at compile)
-- and constrain these access-types to be non-null.
type valid_rotations is array(1..24) of map_handle;
-- Here it is: an access which cannot be null.
type map_handle is Not Null Access Map;
end data_puzzle;
Also, I'd delete the _1 from the discriminants (X,Y,Z) look fine to me.