Ada Programming/Attributes/'Base
Description
[edit | edit source]Represents the base type of another type or subtype. This attribute is used to access attributes of the base type.
T'Base refers to the "base range" of the type, which defines the range in which intermediate calculations are performed.
Base for Integer Types
[edit | edit source]The standard states that the range of T'Base:
- includes the value 0
- is symmetric about zero, with possibly an extra negative value
- includes all of the values in the subtype T
So for example, if T is:
type
Tis
range
1 .. 42;
then the compiler will choose a hardware supported type that includes this range, in this case probably an eight bit type (on a two's complement machine)
type
T'Baseis
range
-128 .. 127;
This definition is not to be taken literally, as by Ada semantics, T and T'Base would be incompatible. In effect, the declaration of T would then be
subtype
Tis
T'Baserange
1 .. 42;
Note that built-in operators go through the base type, and T's "+" op for example is implicitly declared as:
function
"+" (L, R : T'Base)return
T'Base;
There are no constraint checks on T'Base, so for example:
declare
O1 : T := T'(1) + T'(2); O2 : T'Base := T'(1) + T'(2);begin
then in the first assignment to O1, there is a constraint check to ensure that the result of 1 + 2 is in the range of T, but in the second assignment to O2, there is no check.
T'Base is useful for generics, when you need to be able to recover the base range of the type, in order to declare an object with value 0; for example, if this is an accumulator.
It is helpful to know something about the base range of the type, so that you have a guarantee that you don't get any overflow during intermediate calculations. For example, given type T above then:
procedure
Op (O1, O2 : T)is
Sum : T'Base := O1 + O2;begin
This is a problem, since if the sum of O1 and O2 is large (that is, greater than T'Base'Last), then you'll get overflow. Knowing that you're going to be adding two values together means you should declare the type this way:
T_Last :constant
:= 42;type
T_Baseis
0 .. 2 * T_Last;subtype
Tis
T_Baserange
1 .. T_Last;
That way you know that (sub)type T's range is 1 .. 42, but you also have a guarantee that T'Base'Last >= 84, and hence the sum of two values of type T cannot overflow.
Note that a declaration of the form:
type
Tis
range
...
actually declares a subtype, named T, of some anonymous base type. We can refer to the range of this base type as T'Base.
Base for Enumeration Types
[edit | edit source]An enumeration type is its own base type, so given this type:
type
ETis
(A, B, C);
then the range of ET is the same as the range of ET'Base. If you need some extra literals in your "base" type, then you have to declare them manually, not unlike what we did above:
type
ET_Baseis
(ET_Base_First, A, B, C, ET_Base_Last);subtype
ETis
ET_Baserange
A .. C;
Now you can say ET'Succ (ET'Last) and you'll get a meaningful answer. This is necessary when you do something like:
declare
E : ET'Base := ET'First;begin
while
E <= ET'Lastloop
... -- do something E := ET'Succ (E);end
loop
;end
;
Also when you derive from ET_Base with a range constraint, the base of the derived type will include all values of the base type:
type
New_ETis
new
ET_Baserange
A .. C; Correct:constant
Boolean := New_ET'Base'First = ET_Base_First;
Note that here ET_BASE_First is of type New_ET.
Example
[edit | edit source]If you declare:
type
My_Enumis
(Enum1, Enum2, Enum3);
and
subtype
Sub_Enumis
My_Enumrange
Enum1 .. Enum2;
then Sub_Enum'Base'Last is Enum3.