Ada. Time-tested, safe and secure.
Driver on

Ada Programming/Type System – Wikibooks, open books for an open world


Ada. Time-tested, safe and secure.

Ada’s kind system permits the programmer to assemble highly effective abstractions that symbolize the actual world, and to offer worthwhile data to the compiler, in order that the compiler can discover many logic or design errors earlier than they turn into bugs. It’s on the coronary heart of the language, and good Ada programmers be taught to make use of it to nice benefit. 4 ideas govern the sort system:

  • Robust typing: sorts are incompatible with each other, so it’s not potential to combine apples and oranges. There are, nonetheless, methods to transform between sorts.
  • Static typing: kind checked whereas compiling, this enables kind errors to be discovered earlier.
  • Abstraction: sorts symbolize the actual world or the issue at hand; not how the pc represents the info internally. There are methods to specify precisely how a kind have to be represented on the bit stage, however we’ll defer that dialogue to a different chapter.
  • Title equivalence, versus structural equivalence utilized in most different languages. Two sorts are suitable if and provided that they’ve the identical identify; not if they simply occur to have the identical measurement or bit illustration. You possibly can thus declare two integer sorts with the identical ranges which can be completely incompatible, or two report sorts with precisely the identical parts, however that are incompatible.

Varieties are incompatible with each other. Nevertheless, every kind can have any variety of subtypes, that are suitable with their base kind and could also be suitable with each other. See beneath for examples of subtypes that are incompatible with each other.

Predefined sorts[edit]

There are a number of predefined sorts, however most programmers want to outline their very own, application-specific sorts. However, these predefined sorts are very helpful as interfaces between libraries developed independently. The predefined library, clearly, makes use of these sorts too.

These sorts are predefined within the Normal bundle:

Integer
This kind covers not less than the vary

Float
There’s solely a really weak implementation requirement on this kind (RM 3.5.7 (14) (Annotated)); more often than not you’ll outline your personal floating-point sorts, and specify your precision and vary necessities.
Length
A hard and fast level kind used for timing. It represents a time frame in seconds (RM A.1 (43) (Annotated)).
Character
A particular type of Enumerations. There are three predefined sorts of character sorts: 8-bit characters (known as Character), 16-bit characters (known as Wide_Character), and 32-bit characters (Wide_Wide_Character). Character has been current for the reason that first model of the language (Ada 83), Wide_Character was added in Ada 95, whereas the sort Wide_Wide_Character is accessible with Ada 2005.
String
Three indefinite array sorts, of Character, Wide_Character, and Wide_Wide_Character respectively. The usual library incorporates packages for dealing with strings in three variants: mounted size (Ada.Strings.Mounted), with various size beneath a sure higher certain (Ada.Strings.Bounded), and unbounded size (Ada.Strings.Unbounded). Every of those packages has a Wide_ and a Wide_Wide_ variant.
Boolean
A Boolean in Ada is an Enumeration of False and True with particular semantics.

Packages System and System.Storage_Elements predefine some sorts that are primarily helpful for low-level programming and interfacing to {hardware}.

System.Handle
An handle in reminiscence.
System.Storage_Elements.Storage_Offset
An offset, which may be added to an handle to acquire a brand new handle. You can even subtract one handle from one other to get the offset between them. Collectively, Handle, Storage_Offset and their related subprograms present for handle arithmetic.
System.Storage_Elements.Storage_Count
A subtype of Storage_Offset which can’t be unfavorable, and represents the reminiscence measurement of an information construction (much like C’s size_t).
System.Storage_Elements.Storage_Element
In most computer systems, this can be a byte. Formally, it’s the smallest unit of reminiscence that has an handle.
System.Storage_Elements.Storage_Array
An array of Storage_Elements with none which means, helpful when doing uncooked reminiscence entry.

The Kind Hierarchy[edit]

Varieties are organized hierarchically. A sort inherits properties from sorts above it within the hierarchy. For instance, all scalar sorts (integer, enumeration, modular, fixed-point and floating-point sorts) have operators “<“, “>” and arithmetic operators outlined for them, and all discrete sorts can function array indexes.

Here’s a broad overview of every class of sorts; please observe the hyperlinks for detailed explanations. Inside parenthesis there are equivalences in C and Pascal for readers accustomed to these languages.

Signed Integers (int, INTEGER)
Signed Integers are outlined by way of the vary of values wanted.
Unsigned Integers (unsigned, CARDINAL)
Unsigned Integers are known as Modular Varieties. Aside from being unsigned additionally they have wrap-around performance.
Enumerations (enum, char, bool, BOOLEAN)
Ada Enumeration sorts are a separate kind household.
Floating level (float, double, REAL)
Floating level sorts are outlined by the digits wanted, the relative error certain.
Strange and Decimal Mounted Level (DECIMAL)
Mounted level sorts are outlined by their delta, absolutely the error certain.
Arrays ( [ ], ARRAY [ ] OF, STRING )
Arrays with each compile-time and run-time decided measurement are supported.
Report (struct, class, RECORD OF)
A report is a composite kind that teams a number of fields.
Entry (*, ^, POINTER TO)
Ada’s Entry sorts could also be greater than only a easy reminiscence handle.
Activity & Protected (no equivalence in C or Pascal)
Activity and Protected sorts permit the management of concurrency
Interfaces (no equivalence in C or Pascal)
New in Ada 2005, these sorts are much like the Java interfaces.

Classification of Varieties[edit]

Ada’s sorts may be categorised as follows.

Particular vs. Class-wide

kind T is ...  -- a particular kind
  T'Class      -- the corresponding class-wide kind (exists just for tagged sorts)

T'Class and T'Class'Class are the identical.

Primitive operations with parameters of particular sorts are non-dispatching, these with parameters of class-wide sorts are dispatching.

New sorts may be declared by deriving from particular sorts; primitive operations are inherited by derivation. You can’t derive from class-wide sorts.

Constrained vs. Unconstrained

kind I is vary 1 .. 10;           -- constrained
kind AC is array (1 .. 10) of ...  -- constrained
kind AU is array (I vary <>) of ...          -- unconstrained
kind R (X: Discriminant [:= Default]) is ...  -- unconstrained

By giving a constraint to an unconstrained subtype, a subtype or object turns into constrained:

subtype RC is R (Worth);  -- constrained subtype of R
OC: R (Worth);            -- constrained object of nameless constrained subtype of R
OU: R;                    -- unconstrained object

Declaring an unconstrained object is simply potential if a default worth is given within the kind declaration above. The language doesn’t specify how such objects are allotted. GNAT allocates the utmost measurement, in order that measurement adjustments which may happen with discriminant adjustments current no drawback. One other chance is implicit dynamic allocation on the heap and re-allocation adopted by a deallocation when the dimensions adjustments.

Particular vs. Indefinite

kind I is vary 1 .. 10;                     -- particular
kind RD (X: Discriminant := Default) is ...  -- particular
kind T (<>) is ...                    -- indefinite
kind AU is array (I vary <>) of ...  -- indefinite
kind RI (X: Discriminant) is ...      -- indefinite

Particular subtypes permit the declaration of objects with out preliminary worth, since objects of particular subtypes have constraints which can be identified at creation-time. Object declarations of indefinite subtypes want an preliminary worth to provide a constraint; they’re then constrained by the constraint delivered by the preliminary worth.

OT: T  := Expr;                       -- some preliminary expression (object, perform name, and many others.)
OA: AU := (3 => 10, 5 => 2, 4 => 4);  -- index vary is now 3 .. 5
OR: RI := Expr;                       -- once more some preliminary expression as above

Unconstrained vs. Indefinite

Be aware that unconstrained subtypes are usually not essentially indefinite as may be seen above with RD: it’s a particular unconstrained subtype.

Concurrency Varieties[edit]

The Ada language makes use of sorts for yet one more goal along with classifying knowledge
+ operations. The sort system integrates concurrency (threading, parallelism).
Programmers will use sorts for expressing the concurrent
threads of management of their applications.

The core items of this a part of the sort system, the job sorts and
the protected sorts are defined in larger depth in
a bit on tasking.

Restricted Varieties[edit]

Limiting a kind means disallowing task.
The “concurrency sorts” described above are all the time restricted.
Programmers can outline their very own sorts to be restricted, too, like this:

kind T is restricted …;

(The ellipsis stands for personal, or for a report definition,
see the corresponding subsection on this web page.)
A restricted kind additionally would not have
an equality operator until the programmer defines one.

You possibly can be taught extra within the restricted sorts chapter.

Defining new sorts and subtypes[edit]

You possibly can outline a brand new kind with the next syntax:

kind T is...

adopted by the outline of the sort, as defined intimately in every class of kind.

Formally, the above declaration creates a kind and its first subtype named T. The sort itself, appropriately known as the “kind of T”, is nameless; the RM refers to it as T (in italics), however usually speaks sloppily in regards to the kind T. However that is an instructional consideration; for many functions, it’s enough to consider T as a kind.
For scalar sorts, there’s additionally a base kind known as T'Base, which encompasses all values of T.

For signed integer sorts, the kind of T contains the (full) set of mathematical integers. The bottom kind is a sure {hardware} kind, symmetric round zero (aside from presumably one additional unfavorable worth), encompassing all values of T.

As defined above, all sorts are incompatible; thus:

kind Integer_1 is vary 1 .. 10;
kind Integer_2 is vary 1 .. 10;
A : Integer_1 := 8;
B : Integer_2 := A; -- unlawful!

is prohibited, as a result of Integer_1 and Integer_2 are completely different and incompatible sorts. It’s this characteristic which permits the compiler to detect logic errors at compile time, similar to including a file descriptor to a lot of bytes, or a size to a weight. The truth that the 2 sorts have the identical vary doesn’t make them suitable: that is identify equivalence in motion, versus structural equivalence. (Under, we’ll see how one can convert between incompatible sorts; there are strict guidelines for this.)

Creating subtypes[edit]

You can even create new subtypes of a given kind, which will probably be suitable with one another, like this:

kind Integer_1 is vary 1 .. 10;
subtype Integer_2 is Integer_1      vary 7 .. 11;  -- dangerous
subtype Integer_3 is Integer_1'Base vary 7 .. 11;  -- OK
A : Integer_1 := 8;
B : Integer_3 := A; -- OK

The declaration of Integer_2 is dangerous as a result of the constraint 7 .. 11 is just not suitable with Integer_1; it raises Constraint_Error at subtype elaboration time.

Integer_1 and Integer_3 are suitable as a result of they’re each subtypes of the identical kind, particularly Integer_1'Base.

It’s not mandatory that the subtype ranges overlap, or be included in each other. The compiler inserts a run-time vary test once you assign A to B; if the worth of A, at that time, occurs to be exterior the vary of Integer_3, this system raises Constraint_Error.

There are a number of predefined subtypes that are very helpful:

subtype Pure  is Integer vary 0 .. Integer'Final;
subtype Constructive is Integer vary 1 .. Integer'Final;

Derived sorts[edit]

A derived kind is a brand new, full-blown kind created from an current one. Like another kind, it’s incompatible with its dad or mum; nonetheless, it inherits the primitive operations outlined for the dad or mum kind.

kind Integer_1 is vary 1 .. 10;
kind Integer_2 is new Integer_1 vary 2 .. 8;
A : Integer_1 := 8;
B : Integer_2 := A; -- unlawful!

Right here each sorts are discrete; it’s obligatory that the vary of the derived kind be included within the vary of its dad or mum. Distinction this with subtypes. The reason being that the derived kind inherits the primitive operations outlined for its dad or mum, and these operations assume the vary of the dad or mum kind. Right here is an illustration of this characteristic:

process Derived_Types is

   bundle Pak is
      kind Integer_1 is vary 1 .. 10;
      process P (I: in Integer_1); -- primitive operation, assumes 1 .. 10
      kind Integer_2 is new Integer_1 vary 8 .. 10; -- should not break P's assumption
      -- process P (I: in Integer_2);  inherited P implicitly outlined right here
   finish Pak;

   bundle physique Pak is
      -- omitted
   finish Pak;

   use Pak;
   A: Integer_1 := 4;
   B: Integer_2 := 9;

start

   P (B); -- OK, name the inherited operation

finish Derived_Types;

After we name P (B), the parameter B is transformed to Integer_1; this conversion in fact passes for the reason that set of acceptable values for the derived kind (right here, 8 .. 10) have to be included in that of the dad or mum kind (1 .. 10). Then P is known as with the transformed parameter.

Contemplate nonetheless a variant of the instance above:

process Derived_Types is

  bundle Pak is
    kind Integer_1 is vary 1 .. 10;
    process P (I: in Integer_1; J: out Integer_1);
    kind Integer_2 is new Integer_1 vary 8 .. 10;
  finish Pak;

  bundle physique Pak is
    process P (I: in Integer_1; J: out Integer_1) is
    start
      J := I - 1;
    finish P;
  finish Pak;

  use Pak;

  A: Integer_1 := 4;  X: Integer_1;
  B: Integer_2 := 8;  Y: Integer_2;

start

  P (A, X);
  P (B, Y);

finish Derived_Types;

When P (B, Y) is known as, each parameters are transformed to Integer_1. Thus the vary test on J (7) within the physique of P will move. Nevertheless on return parameter Y is transformed again to Integer_2 and the vary test on Y will in fact fail.

With the above in thoughts, you will note why within the following program Constraint_Error will probably be known as at run time, earlier than P is even known as.

process Derived_Types is

  bundle Pak is
    kind Integer_1 is vary 1 .. 10;
    process P (I: in Integer_1; J: out Integer_1);
    kind Integer_2 is new Integer_1'Base vary 8 .. 12;
  finish Pak;

  bundle physique Pak is
    process P (I: in Integer_1; J: out Integer_1) is
    start
      J := I - 1;
    finish P;
  finish Pak;

  use Pak;

  B: Integer_2 := 11;  Y: Integer_2;

start

  P (B, Y);

finish Derived_Types;

Subtype classes[edit]

Ada helps numerous classes of subtypes which have completely different talents. Right here is an outline in alphabetical order.

Nameless subtype[edit]

A subtype which doesn’t have a reputation assigned to it. Such a subtype is created with a variable declaration:

X : String (1 .. 10) := (others => ' ');

Right here, (1 .. 10) is the constraint. This variable declaration is equal to:

subtype Anonymous_String_Type is String (1 .. 10);

X : Anonymous_String_Type := (others => ' ');

Base kind[edit]

In Ada, all sorts are nameless and solely subtypes could also be named.
For scalar sorts, there’s a particular subtype of the nameless kind, known as the base kind, which is nameable with the ‘Base attribute.
The bottom kind contains all values of the first subtype. Some examples:

 kind Int is vary 0 .. 100;

The bottom kind Int'Base is a {hardware} kind chosen by the compiler that contains the values of Int. Thus it might have the vary -27 .. 27-1 or -215 .. 215-1 or another such kind.

 kind Enum  is (A, B, C, D);
 kind Quick is new Enum vary A .. C;

Enum'Base is similar as Enum, however Quick'Base additionally holds the literal D.

Constrained subtype[edit]

A subtype of an indefinite subtype that provides constraints. The next instance defines a 10 character string sub-type.

 subtype String_10 is String (1 .. 10);

You can’t partially constrain an unconstrained subtype:

 kind My_Array is array (Integer vary <>, Integer vary <>) of Some_Type;

 -- subtype Constr is My_Array (1 .. 10, Integer vary <>);  unlawful

 subtype Constr is My_Array (1 .. 10, -100 .. 200);

Constraints for all indices have to be given, the result’s essentially a particular subtype.

Particular subtype[edit]

A particular subtype is a subtype whose measurement is thought at compile-time. All subtypes which aren’t indefinite subtypes are, by definition, particular subtypes.

Objects of particular subtypes could also be declared with out further constraints.

Indefinite subtype[edit]

An indefinite subtype is a subtype whose measurement is just not identified at compile-time however is dynamically calculated at run-time. An indefinite subtype doesn’t by itself present sufficient data to create an object; a further constraint or specific initialization expression is important as a way to calculate the precise measurement and due to this fact create the article.

X : String := "It is a string";

X is an object of the indefinite (sub)kind String. Its constraint is derived implicitly from its preliminary worth. X might change its worth, however not its bounds.

It needs to be famous that it’s not essential to initialize the article from a literal. You can even use a perform. For instance:

X : String := Ada.Command_Line.Argument (1);

This assertion reads the primary command-line argument and assigns it to X.

A subtype of an indefinite subtype that doesn’t add a constraint solely introduces a brand new identify for the unique subtype (a form of renaming below a distinct notion).

 subtype My_String is String;

My_String and String are interchangeable.

Named subtype[edit]

A subtype which has a reputation assigned to it. “First subtypes” are created with the key phrase kind (keep in mind that sorts are all the time nameless, the identify in a kind declaration is the identify of the primary subtype), others with the key phrase subtype. For instance:

kind Count_To_Ten is vary 1 .. 10;

Count_to_Ten is the primary subtype of an acceptable integer base kind.
Nevertheless, if you want to make use of this as an index constraint on String, the next declaration is prohibited:

subtype Ten_Characters is String (Count_to_Ten);

It’s because String has Constructive as its index, which is a subtype of Integer (these declarations are taken from bundle Normal):

subtype Constructive is Integer vary 1 .. Integer'Final;

kind String is (Constructive vary <>) of Character;

So you need to use the next declarations:

subtype Count_To_Ten is Integer vary 1 .. 10;
subtype Ten_Characters is String (Count_to_Ten);

Now Ten_Characters is the identify of that subtype of String which is constrained to Count_To_Ten.
You see that posing constraints on sorts versus subtypes has very completely different results.

Unconstrained subtype[edit]

Any indefinite kind can also be an unconstrained subtype. Nevertheless, unconstrainedness and indefiniteness are usually not the identical.

 kind My_Enum is (A, B, C);
 kind My_Record (Discriminant: My_Enum) is ...;

 My_Object_A: My_Record (A);

This kind is unconstrained and indefinite as a result of you must give an precise discriminant for object declarations; the article is constrained to this discriminant which can not change.

When nonetheless a default is supplied for the discriminant, the sort is particular but unconstrained; it permits to outline each, constrained and unconstrained objects:

 kind My_Enum is (A, B, C);
 kind My_Record (Discriminant: My_Enum := A) is ...;

 My_Object_U: My_Record;      -- unconstrained object
 My_Object_B: My_Record (B);  -- constrained to discriminant B like above

Right here, My_Object_U is unconstrained; upon declaration, it has the discriminant A (the default) which nonetheless might change.

Incompatible subtypes[edit]

 kind My_Integer is vary -10 .. + 10;
 subtype My_Positive is My_Integer vary + 1 .. + 10;
 subtype My_Negative is My_Integer vary -10 .. -  1;

These subtypes are in fact incompatible.

One other instance are subtypes of a discriminated report:

 kind My_Enum is (A, B, C);
 kind My_Record (Discriminant: My_Enum) is ...;
 subtype My_A_Record is My_Record (A);
 subtype My_C_Record is My_Record (C);

Additionally these subtypes are incompatible.

Certified expressions[edit]

Usually, the compiler is ready to infer the kind of an expression; for instance:

kind Enum is (A, B, C);
E : Enum := A;

Right here the compiler is aware of that A is a price of the sort Enum. However contemplate:

process Unhealthy is
   kind Enum_1 is (A, B, C);
   process P (E : in Enum_1) is... -- omitted
   kind Enum_2 is (A, X, Y, Z);
   process P (E : in Enum_2) is... -- omitted
start
   P (A); -- unlawful: ambiguous
finish Unhealthy;

The compiler can’t select between the 2 variations of P; each can be equally legitimate. To take away the anomaly, you employ a certified expression:

   P (Enum_1'(A)); -- OK

As seen within the following instance, this syntax is usually used when creating new objects. For those who attempt to compile the instance, it would fail with a compilation error for the reason that compiler will decide that 256 is just not in vary of Byte.

with Ada.Text_IO;

process Convert_Evaluate_As is
   kind Byte     is mod 2**8;
   kind Byte_Ptr is entry Byte;

   bundle T_IO renames Ada.Text_IO;
   bundle M_IO is new Ada.Text_IO.Modular_IO (Byte);

   A : fixed Byte_Ptr := new Byte'(256);
start
   T_IO.Put ("A = ");
   M_IO.Put (Merchandise  => A.all,
             Width =>  5,
             Base  => 10);
finish Convert_Evaluate_As;

You need to use certified expression when getting a string literal’s size.

"foo"'Size                  -- compilation error: prefix of attribute have to be a reputation
                              --                    qualify expression to show it into a reputation
String'("foo" & "bar")'Size -- 6

Kind conversions[edit]

Information don’t all the time come within the format you want them. You should, then, face the duty of changing them. As a real multi-purpose language with a particular emphasis on “mission important”, “system programming” and “security”, Ada has a number of conversion methods. Essentially the most troublesome half is choosing the proper one, so the next checklist is sorted so as of utility. You need to attempt the primary one first; the final method is a final resort, for use if all others fail. There are additionally a number of associated methods that you simply would possibly select as a substitute of really changing the info.

Since an important side is just not the results of a profitable conversion, however how the system will react to an invalid conversion, all examples additionally reveal defective conversions.

Express kind conversion[edit]

An specific kind conversion seems very like a perform name; it doesn’t use the tick (apostrophe, ‘) just like the certified expression does.

Type_Name (Expression)

The compiler first checks that the conversion is authorized, and whether it is, it inserts a run-time test on the level of the conversion; therefore the identify checked conversion. If the conversion fails, this system raises Constraint_Error. Most compilers are very good and optimise away the constraint checks; so, you needn’t fear about any efficiency penalty. Some compilers can even warn {that a} constraint test will all the time fail (and optimise the test with an unconditional increase).

Express kind conversions are authorized:

  • between any two numeric sorts
  • between any two subtypes of the identical kind
  • between any two sorts derived from the identical kind (word particular guidelines for tagged sorts)
  • between array sorts below sure circumstances (see RM 4.6(24.2/2..24.7/2))
  • and nowhere else

(The principles turn into extra complicated with class-wide and nameless entry sorts.)

I: Integer := Integer (10);  -- Pointless specific kind conversion
J: Integer := 10;            -- Implicit conversion from common integer
Ok: Integer := Integer'(10);  -- Use the worth 10 of kind Integer: certified expression
                             -- (qualification not mandatory right here).

This instance illustrates specific kind conversions:

with Ada.Text_IO;

process Convert_Checked is
   kind Quick is vary -128 .. +127;
   kind Byte  is mod 256;

   bundle T_IO renames Ada.Text_IO;
   bundle I_IO is new Ada.Text_IO.Integer_IO (Quick);
   bundle M_IO is new Ada.Text_IO.Modular_IO (Byte);

   A : Quick := -1;
   B : Byte;
start
   B := Byte (A);  -- vary test will result in Constraint_Error
   T_IO.Put ("A = ");
   I_IO.Put (Merchandise  =>  A,
             Width =>  5,
             Base  => 10);
   T_IO.Put (", B = ");
   M_IO.Put (Merchandise  =>  B,
             Width =>  5,
             Base  => 10);
finish Convert_Checked;

Express conversions are potential between any two numeric sorts: integers, fixed-point and floating-point sorts. If one of many sorts concerned is a fixed-point or floating-point kind, the compiler not solely checks for the vary constraints (thus the code above will increase Constraint_Error), but in addition performs any lack of precision mandatory.

Instance 1: the lack of precision causes the process to solely ever print “0” or “1”, since P / 100 is an integer and is all the time zero or one.

with Ada.Text_IO;
process Naive_Explicit_Conversion is
   kind Proportion is digits 4 vary 0.0 .. 1.0;
   kind Proportion is vary 0 .. 100;
   perform To_Proportion (P : in Proportion) return Proportion is
   start
      return Proportion (P / 100);
   finish To_Proportion;
start
   Ada.Text_IO.Put_Line (Proportion'Picture (To_Proportion (27)));
finish Naive_Explicit_Conversion;

Instance 2: we use an intermediate floating-point kind to ensure the precision.

with Ada.Text_IO;
process Explicit_Conversion is
   kind Proportion is digits 4 vary 0.0 .. 1.0;
   kind Proportion is vary 0 .. 100;
   perform To_Proportion (P : in Proportion) return Proportion is
      kind Prop is digits 4 vary 0.0 .. 100.0;
   start
      return Proportion (Prop (P) / 100.0);
   finish To_Proportion;
start
   Ada.Text_IO.Put_Line (Proportion'Picture (To_Proportion (27)));
finish Explicit_Conversion;

You would possibly ask why you need to convert between two subtypes of the identical kind. An instance will illustrate this.

subtype String_10 is String (1 .. 10);
X: String := "A line lengthy sufficient to make the instance legitimate";
Slice: fixed String := String_10 (X (11 .. 20));

Right here, Slice has bounds 1 and 10, whereas X (11 .. 20) has bounds 11 and 20.

Change of Illustration[edit]

Kind conversions can be utilized for packing and unpacking of information or arrays.

kind Unpacked is report
  -- any parts
finish report;

kind Packed is new Unpacked;
for  Packed use report
  -- part clauses for some or for all parts
finish report;
P: Packed;
U: Unpacked;

P := Packed (U);  -- packs U
U := Unpacked (P);  -- unpacks P

Checked conversion for non-numeric sorts[edit]

The examples above all revolved round conversions between numeric sorts; it’s potential to transform between any two numeric sorts on this manner. However what occurs between non-numeric sorts, e.g. between array sorts or report sorts? The reply is two-fold:

  • you may convert explicitly between a kind and kinds derived from it, or between sorts derived from the identical kind,
  • and that is all. No different conversions are potential.

Why would you wish to derive a report kind from one other report kind? Due to illustration clauses. Right here we enter the realm of low-level programs programming, which isn’t for the faint of coronary heart, neither is it helpful for desktop purposes. So maintain on tight, and let’s dive in.

Suppose you have got a report kind which makes use of the default, environment friendly illustration. Now you wish to write this report to a tool, which makes use of a particular report format. This particular illustration is extra compact (makes use of fewer bits), however is grossly inefficient. You wish to have a layered programming interface: the higher layer, supposed for purposes, makes use of the environment friendly illustration. The decrease layer is a tool driver that accesses the {hardware} straight and makes use of the inefficient illustration.

bundle Device_Driver is
   kind Size_Type is vary 0 .. 64;
   kind Register is report
      A, B : Boolean;
      Dimension : Size_Type;
   finish report;

   process Learn (R : out Register);
   process Write (R : in Register);
finish Device_Driver;

The compiler chooses a default, environment friendly illustration for Register. For instance, on a 32-bit machine, it might in all probability use three 32-bit phrases, one for A, one for B and one for Dimension. This environment friendly illustration is sweet for purposes, however at one level we wish to convert the whole report to simply Eight bits, as a result of that is what our {hardware} requires.

bundle physique Device_Driver is
   kind Hardware_Register is new Register; -- Derived kind.
   for Hardware_Register use report
      A at 0 vary 0 .. 0;
      B at 0 vary 1 .. 1;
      Dimension at 0 vary 2 .. 7;
   finish report;

   perform Get return Hardware_Register; -- Physique omitted
   process Put (H : in Hardware_Register); -- Physique omitted

   process Learn (R : out Register) is
      H : Hardware_Register := Get;
   start
      R := Register (H); -- Express conversion.
   finish Learn;

   process Write (R : in Register) is
   start
      Put (Hardware_Register (R)); -- Express conversion.
   finish Write;
finish Device_Driver;

Within the above instance, the bundle physique declares a derived kind with the inefficient, however compact illustration, and converts to and from it.

This illustrates that kind conversions may end up in a change of illustration.

View conversion, in object-oriented programming[edit]

Inside object-oriented programming you need to distinguish between particular sorts and class-wide sorts.

With particular sorts, solely conversions within the path to the basis are potential, which in fact can’t fail. There are not any conversions in the wrong way (the place would you get the additional parts from?); extension aggregates have for use as a substitute.

With the conversion itself, no parts of the supply object that aren’t current within the goal object are misplaced, they’re simply hidden from visibility. Subsequently, this type of conversion is known as a view conversion because it supplies a view of the supply object as an object of the goal kind (particularly it doesn’t change the article’s tag).

It’s a frequent idiom in object oriented programming to rename the results of a view conversion. (A renaming declaration doesn’t create a brand new object; it solely provides a brand new identify to one thing that already exists.)

kind Parent_Type is tagged report
   ;
finish report;
kind Child_Type is new Parent_Type with report
   ;
finish report;

Child_Instance : Child_Type;
Parent_View    : Parent_Type renames Parent_Type (Child_Instance);
Parent_Part    : Parent_Type := Parent_Type (Child_Instance);

Parent_View is just not a brand new object, however one other identify for Child_Instance seen because the dad or mum, i.e. solely the dad or mum parts are seen, the child-specific parts are hidden. Parent_Part, nonetheless, is an object of the dad or mum kind, which in fact has no storage for the child-specific parts, so they’re misplaced with the task.

All sorts derived from a tagged kind T kind a tree rooted at T. The category-wide kind T'Class can maintain any object inside this tree. With class-wide sorts, conversions in any path are potential; there’s a run-time tag test that raises Constraint_Error if the test fails. These conversions are additionally view conversions, no knowledge is created or misplaced.

Object_1 : Parent_Type'Class := Parent_Type'Class (Child_Instance);
Object_2 : Parent_Type'Class renames Parent_Type'Class (Child_Instance);

Object_1 is a brand new object, a replica; Object_2 is only a new identify. Each objects are of the class-wide kind. Conversions to any kind throughout the given class are authorized, however are tag-checked.

Success : Child_Type := Child_Type (Parent_Type'Class (Parent_View));
Failure : Child_Type := Child_Type (Parent_Type'Class (Parent_Part));

The primary conversion passes the tag test and each objects Child_Instance and Success are equal. The second conversion fails the tag test. (Conversion assignments of this type will not often be used; dispatching will do that robotically, see object oriented programming.)

You possibly can carry out these checks your self with membership checks:

if Parent_View in Child_Type then ...
if Parent_View in Child_Type'Class then ...

There’s additionally the bundle Ada.Tags.

Handle conversion[edit]

Ada’s entry kind isn’t just a reminiscence location (a skinny pointer). Relying on implementation and the entry kind used, the entry would possibly maintain further data (a fats pointer). For instance GNAT retains two reminiscence addresses for every entry to an indefinite object — one for the info and one for the constraint informations (Dimension, First, Final).

If you wish to convert an entry to a easy reminiscence location you should use the bundle System.Address_To_Access_Conversions.
Be aware nonetheless that an handle and a fats pointer can’t be transformed reversibly into each other.

The handle of an array object is the handle of its first part. Thus the bounds get misplaced in such a conversion.

kind My_Array is array (Constructive vary <>) of One thing;
A: My_Array (50 .. 100);

     A'Handle = A(A'First)'Handle

Unchecked conversion[edit]

One of many nice criticisms of Pascal was “there is no such thing as a escape”. The rationale was that generally you need to convert the incompatible. For this goal, Ada has the generic perform Unchecked_Conversion:

generic
   kind Supply (<>) is restricted personal;
   kind Goal (<>) is restricted personal;
perform Ada.Unchecked_Conversion (S : Supply) return Goal;

Unchecked_Conversion will bit-copy the supply knowledge and reinterpret them below the goal kind with none checks. It’s your chore to ensure that the necessities on unchecked conversion as acknowledged in RM 13.9 (Annotated) are fulfilled; if not, the result’s implementation dependent and will even result in irregular knowledge. Use the ‘Legitimate attribute after the conversion to test the validity of the info in problematic circumstances.

A perform name to (an occasion of) Unchecked_Conversion will copy the supply to the vacation spot. The compiler may additionally do a conversion in place (each occasion has the conference Intrinsic).

To make use of Unchecked_Conversion you must instantiate the generic.

Within the instance beneath, you may see how that is performed. When run, the instance will output A = -1, B = 255. No error will probably be reported, however is that this the outcome you anticipate?

with Ada.Text_IO;
with Ada.Unchecked_Conversion;

process Convert_Unchecked is

   kind Quick is vary -128 .. +127;
   kind Byte  is mod 256;

   bundle T_IO renames Ada.Text_IO;
   bundle I_IO is new Ada.Text_IO.Integer_IO (Quick);
   bundle M_IO is new Ada.Text_IO.Modular_IO (Byte);

   perform Convert is new Ada.Unchecked_Conversion (Supply => Quick,
                                                     Goal => Byte);

   A : fixed Quick := -1;
   B : Byte;

start

   B := Convert (A);
   T_IO.Put ("A = ");
   I_IO.Put (Merchandise  =>  A,
             Width =>  5,
             Base  => 10);
   T_IO.Put (", B = ");
   M_IO.Put (Merchandise  =>  B,
             Width =>  5,
             Base  => 10);

finish Convert_Unchecked;

There’s in fact a variety test within the task B := Convert (A);. Thus if B have been outlined as B: Byte vary 0 .. 10;, Constraint_Error can be raised.

Overlays[edit]

If the copying of the results of Unchecked_Conversion is an excessive amount of waste by way of efficiency, then you may attempt overlays, i.e. handle mappings. By utilizing overlays, each objects share the identical reminiscence location. For those who assign a price to 1, the opposite adjustments as properly. The syntax is:

for Goal'Handle use expression;
pragma Import (Ada, Goal);

the place expression defines the handle of the supply object.

Whereas overlays would possibly look extra elegant than Unchecked_Conversion, you need to be conscious that they’re much more harmful and have even larger potential for doing one thing very unsuitable. For instance if Supply'Dimension < Goal'Dimension and also you assign a price to Goal, you would possibly inadvertently write into reminiscence allotted to a distinct object.

It’s a must to take care additionally of implicit initializations of objects of the goal kind, since they might overwrite the precise worth of the supply object. The Import pragma with conference Ada can be utilized to stop this, because it avoids the implicit initialization, RM B.1 (Annotated).

The instance beneath does the identical as the instance from “Unchecked Conversion”.

with Ada.Text_IO;

process Convert_Address_Mapping is
   kind Quick is vary -128 .. +127;
   kind Byte  is mod 256;

   bundle T_IO renames Ada.Text_IO;
   bundle I_IO is new Ada.Text_IO.Integer_IO (Quick);
   bundle M_IO is new Ada.Text_IO.Modular_IO (Byte);

   A : aliased Quick;
   B : aliased Byte;
  
   for B'Handle use A'Handle;
  pragma Import (Ada, B);
  
start
   A := -1;
   T_IO.Put ("A = ");
   I_IO.Put (Merchandise  =>  A,
             Width =>  5,
             Base  => 10);
   T_IO.Put (", B = ");
   M_IO.Put (Merchandise  =>  B,
             Width =>  5,
             Base  => 10);
finish Convert_Address_Mapping;

Export / Import[edit]

Only for the report: There’s nonetheless one other technique utilizing the Export and Import pragmas. Nevertheless, since this technique fully undermines Ada’s visibility and kind ideas much more than overlays, it has no place right here on this language introduction and is left to specialists.

Elaborated Dialogue of Varieties for Signed Integer Varieties[edit]

As defined earlier than, a kind declaration

kind T is vary 1 .. 10;

declares an nameless kind T and its first subtype T (please word the italicization). T encompasses the whole set of mathematical integers. Static expressions and named numbers make use of this truth.

All numeric integer literals are of kind Universal_Integer. They’re transformed to the suitable particular kind the place wanted. Universal_Integer itself has no operators.

Some examples with static named numbers:

 S1: fixed := Integer'Final + Integer'Final;       -- "+" of Integer
 S2: fixed := Long_Integer'Final + 1;             -- "+" of Long_Integer
 S3: fixed := S1 + S2;                           -- "+" of root_integer
 S4: fixed := Integer'Final + Long_Integer'Final;  -- unlawful

Static expressions are evaluated at compile-time on the suitable sorts with no overflow checks, i.e. mathematically actual (solely restricted by pc retailer). The result’s then implicitly transformed to Universal_Integer.

The literal 1 in S2 is of kind Universal_Integer and implicitly transformed to Long_Integer.

S3 implicitly converts the summands to root_integer, performs the calculation and converts again to Universal_Integer.

S4 is prohibited as a result of it mixes two differing kinds. You possibly can nonetheless write this as

 S5: fixed := Integer'Pos (Integer'Final) + Long_Integer'Pos (Long_Integer'Final);  -- "+" of root_integer

the place the Pos attributes convert the values to Universal_Integer, that are then additional implicitly transformed to root_integer, added and the outcome transformed again to Universal_Integer.

root_integer is the nameless biggest integer kind representable by the {hardware}. It has the vary System.Min_Integer .. System.Max_Integer. All integer sorts are rooted at root_integer, i.e. derived from it. Universal_Integer may be seen as root_integer'Class.

Throughout run-time, computations in fact are carried out with vary checks and overflow checks on the suitable subtype. Intermediate outcomes might nonetheless exceed the vary limits. Thus with I, J, Ok of the subtype T above, the next code will return the right outcome:

I := 10;
J :=  8;
Ok := (I + J) - 12;
-- I := I + J;  -- vary test would fail, resulting in Constraint_Error

Actual literals are of kind Universal_Real, and related guidelines as those above apply accordingly.

Relations between sorts[edit]

Varieties may be created from different sorts. Array sorts, for instance, are created from two sorts, one for the arrays’ index and one for the arrays’ parts. An array, then, expresses an affiliation, particularly that between one worth of the index kind and a price of the part kind.

 kind Coloration is (Purple, Inexperienced, Blue);
 kind Depth is vary 0 .. 255;
 
 kind Colored_Point is array (Coloration) of Depth;

The sort Coloration is the index kind and the sort Depth is the part kind of the array kind Colored_Point. See array.

See additionally[edit]

Wikibook[edit]

Ada Reference Guide[edit]

Leave a Reply

Your email address will not be published. Required fields are marked *