Menu

[fca612]: / ada-latex / chapter5.tex  Maximize  Restore  History

Download this file

765 lines (606 with data), 30.3 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
\chapter{Type system}
Ada's type system allows the programmer to construct powerful
abstractions that represent the real world, and to provide valuable
information to the compiler, so that the compiler can find many logic
or design errors before they become bugs. It is at the heart of the
language, and good Ada programmers learn to use it to great advantage.
Four principles govern the type system:
\begin{description}
\item[String typing] Types are incompatible with one another, so it is
not possible to mix apples and oranges. There are, however, ways to
convert between types.
\item[Static typing] The programmer must declare the type of all
objects, so that the compiler can check them. This is in contrast to
dynamically typed languages such as Python, Smalltalk, or Lisp.
Also, there is no type inference like in OCaml.
\item[Abstraction] Types represent the real world or the problem at
hand; not how the computer represents the data internally. There are
ways to specify exactly how a type must be represented at the bit
level, but we will defer that discussion to another chapter.
\item[Name Equivalence] Two types are compatible if and only if they
have the same name; not if they just happen to have the same size or
bit representation. You can thus declare two integer types with the
same ranges that are totally incompatible, or two record types with
the exact same components, but which are incompatible.
\end{description}
Types are incompatible with one another. However, each type can have
any number of subtypes, which are compatible with one another, and
with their base type.
\section{Predefined types}
\begin{description}
\item[Integer] This type covers at least the range $-2^{15}+1 ..
+2^{15}-1$ The
standard\footnote{http://www.adaic.com/standards/95lrm/html/RM-3-5-4.html}
also defines Natural and Positive subtypes of this type. Natural
starts at 0, positive starts at 1.
\item[Float] There is only a very weak implementation requirement on
this
type\footnote{http://www.adaic.com/standards/95lrm/html/RM-3-5-7.html};
most of the time you would define your own floating-point types, and
specify your precision and range requirements.
\item[Duration] A fixed point type used for timing. It represents a
period of time in seconds
\footnote{http://www.adaic.com/standards/95lrm/html/RM-A-1.html}
\item[Character] A special form of enumerations. There are two
predefined kinds of character types: 8-bit characters (called
Character) and 16-bit characters (called Wide\_Character). Ada 2005
adds a 32-bit character type called Wide\_Wide\_Character.
\item[String and Wide\_String] These are two undefinite array types,
of Character and Wide\_Char\-ac\-ter respectively. Ada 2005 adds a
Wide\_Wide\_String type. The standard library contains packages for
handling strings in three variants: fixed length
(Ada.Strings.Fixed), with varying length below a certain upper bound
(Ada.Strings.Bounded), and unbounded length (Ada.Strings.Unbounded).
Each of these packages has a Wide\_ and a Wide\_Wide\_ variant.
\item[Boolean] A Boolean in Ada is an Enumeration of False and True
with special semantics.
\end{description}
Packages System and System.Storage\_Elements predefine some types
which are primarily useful for low-level programming and interfacing
to hardware.
\begin{description}
\item[System.Address] an address in memory.
\item[System.Storage\_Elements.Storage\_Offset] an offset, which can
be added to an address to obtain a new address. You can also
substract one address from another to get the offset between them.
Together, Address, Storage\_Offset and their associated subprograms
provide for address arithmetic.
\item[System.Storage\_Elements.Storage\_Count] a subtype of
Storage\_Offset which cannot be negative, and represents the memory
size of a data structure (similar to C's size\_t).
\item[System.Storage\_Elements.Storage\_Element] in most computers,
this is a byte. Formally, it is the smallest unit of memory that has
an address.
\item[System.Storage\_Elements.Storage\_Array] an array of
Storage\_Elements without any meaning, useful when doing raw memory
access.
\end{description}
\section{The Type Hierarchy}
Types are organised hierarchically. A type inherits properties from
types above it in the hierarchy. For example, all scalar types
(integer, enumeration, modular, fixed-point and floating-point types)
have operators ``$<$'', ``$>$'' and arithmetic operators defined for
them, and all discrete types can serve as array indexes.
\begin{figure}[!htbp]
\includegraphics[width=\textwidth]{Ada_types.png}
\caption{Ada's type hierarchy}
\label{fig:types}
\end{figure}
Here is a broad overview of each category of types; please follow the
links for detailed explanations. Inside parenthesis there are
equivalences in C and Pascal for readers familiar with those
languages.
\begin{description}
\newcommand{\myitem}[2]{\item[#1] \textsl{\footnotesize #2}}
\myitem{Signed Integers}{(int, INTEGER)} Signed Integers are defined
via the range of values needed. \myitem{Unsigned
Integers}{(unsigned, CARDINAL)} Unsigned Integers are called
Modular Types. Apart from being unsigned they also have wrap-around
functionality. \myitem{Enumerations}{(enum, char, bool, BOOLEAN)}
Ada Enumeration types are a separate type family. \myitem{Floating
point} {(float, double, REAL)} Floating point values are defined
by the digits needed, the relative error bound. \myitem{Ordinary
and Decimal Fixed Point}{(DECIMAL)} Fixed point type are defined
by their delta, the absolute error bound. \myitem{Arrays} {([],
ARRAY [] OF, STRING)} Arrays with both compile-time and run-time
determined size are supported. \myitem{Record} {(struct, class,
RECORD OF)} A record is a composite type that groups one or more
fields. \myitem{Access} {(*, \^, POINTER TO)} Ada's Access types
may be more than just a simple memory address. \myitem{Task \&
Protected} {(no equivalence in C or Pascal)} Task and Protected
types allow the control of concurrency \myitem{Interfaces}{(no
equivalence in C or Pascal)}
\end{description}
\section{Concurrency Types}
The Ada language uses types for one more purpose in addition to
classifying data + operations. The type system integrates concurrency
(threading, parallelism). Programmers will use types for expressing
the concurrent threads of control of their programs.
The core pieces of this part of the type system, the task types and
the protected types are explained in greater depth in the tasking
chapter\ref{ch:tasking}.
\section{Limited Types}
Limiting a type means disallowing assignment. The ``concurreny types''
described above are always limited. Programmers can define their own
types to be limited, too, like this: \lstinline|type T is limited ...;|
(The ellipsis stands for private, or for a record definition, see
the corresponding subsection on this page.) A limited type also
doesn't have an equality operator unless the programmer defines one.
\section{Defining new types and subtypes}
You can define a new type with the following syntax:
\lstinline|type T is...| followed by the description of the type,
as explained in detail in each category of type.
Formally, the above declaration creates a type and its first subtype
named T. The type itself is named T'Base. But this is an academic
consideration; for most purposes, it is sufficient to think of T as a
type.
As explained above, all types are incompatible; thus:
\begin{mylstlisting}
type Integer_1 is range 1 .. 10;
type Integer_2 is range 1 .. 10;
A : Integer_1 := 8;
B : Integer_2 := A; -- illegal!
\end{mylstlisting}
\noindent is illegal, because Integer\_1 and Integer\_2 are different and
incompatible types. It is this feature which allows the compiler to
detect logic errors at compile time, such as adding a file descriptor
to a number of bytes, or a length to a weight. The fact that the two
types have the same range does not make them compatible: this is name
equivalence in action, as opposed to structural equivalence. (Below,
we will see how you can convert between incompatible types; there are
strict rules for this.)
\subsection{Creating subtypes}
You can also create new subtypes of a given type, which will be
compatible with each other, like this:
\begin{mylstlisting}
type Integer_1 is range 1 .. 10;
subtype Integer_2 is Integer_1 range 7 .. 11;
A : Integer_1 := 8;
B : Integer_2 := A; -- OK
\end{mylstlisting}
Now, \lstinline!Integer_1! and \lstinline{Integer_2} are compatible
because they are both subtypes of the same type, namely
\lstinline{Integer_1'Base}.
It is not necessary that the ranges overlap, or be included in one
another. The compiler inserts a run-time range check when you assign
A to B; if the value of A, at that point, happens to be outside the
range of \lstinline{Integer_2}, the program raises
\lstinline{Constraint_Error}. This is called an \emph{implicit type
conversion''}.
There are a few predefined subtypes which are very useful:
\begin{mylstlisting}
subtype Natural is Integer range 0 .. Integer'Last;
subtype Positive is Integer range 1 .. Integer'Last;
\end{mylstlisting}
\subsection{Derived types}
A derived type is a new, full-blown type created from an existing one.
Like any other type, it is incompatible with its parent; however, it
inherits the primitive operations defined for the parent type.
\begin{mylstlisting}
type Integer_1 is range 1 .. 10;
type Integer_2 is new Integer_1 range 2 .. 8;
A : Integer_1 := 8;
B : Integer_2 := A; -- illegal!
\end{mylstlisting}
Here both types are discrete; it is mandatory that the range of the
derived type be included in the range of its parent. Contrast this
with subtypes. The reason is that the derived type inherits the
primitive operations defined for its parent, and these operations
assume the range of the parent type. Here is an illustration of this
feature:
\begin{mylstlisting}
procedure Derived_Types is
package Pak is
type Integer_1 is range 1 .. 10;
procedure P (I : in Integer_1); -- primitive operation, assumes 1 .. 10
type Integer_2 is new Integer_1 range 8 .. 10; -- must not break P's assumption
end Pak;
package body Pak is
-- omitted
end Pak;
use Pak;
A : Integer_1 := 4;
B : Integer_2 := 9;
begin
P (B); -- OK, call the inherited operation
end Derived_Types;
\end{mylstlisting}
When we call
\lstinline{P (B)}, there is \emph{no} type conversion; we call a completely new version of \lstinline{P},
which accepts only parameters of type \lstinline{Integer_2}. Since there
is no type conversion, there is no range check either. This is why
the set of acceptable values for the derived type (here, 8 .. 10) must
be included in that of the parent type (1 .. 10).
\section{Subtype categories}
Ada supports various categories of subtypes which have different
abilities. Here is an overview in alphabetical order.
\subsection{Anonymous subtype}
A subtype which does not have a name assigned to it. Such a subtype is
created with a variable declaration:
\begin{mylstlisting}
X : String (1 .. 10) := others => ' ');
\end{mylstlisting}
Here, (1 .. 10) is the constraint. This variable declaration is equivalent to:
\begin{mylstlisting}
subtype Anonymous_String_Type is String (1 .. 10);
X : Anonymous_String_Type := others => ' ');
\end{mylstlisting}
\subsection{Base type}
Within this document we almost always speak of subtypes. But where
there is a subtype there must also be a base type from which it is
derived. In Ada all base types are anonymous and only subtypes may be
named.
But it is still possible to use base type by the use of the 'Base attribute.
\subsection{Constrained subtype}
A subtype of an indefinite subtype that adds a constraint. The
following example defines a 10 character string sub-type.
\begin{mylstlisting}
subtype String_10 is String (1 .. 10);
\end{mylstlisting}
If all constraints of an original indefinite subtype are defined then
the new sub-type is a definite subtype.
\subsection{Definite subtype}
A definite subtype is a subtype whose size is known at compile-time.
All subtypes which are not indefinite subtypes are, by definition,
definite subtypes.
Objects of definite subtypes may be declared without additional
constraints.
\subsection{Indefinite subtype}
An \emph{indefinite subtype} is a subtype whose size is not known at
compile-time but is dynamically calculated at run-time. An indefinite
subtype does not by itself provide enough information to create an
object; an additional constraint or explicit initialization expression
is necessary in order to calculate the actual size and therefore
create the object.
\begin{mylstlisting}
X : String := "This is a string";
\end{mylstlisting}
X is an object of the indefinite (sub)type String. Its constraint is
derived implicitly from its initial value. X may change its value, but
not its bounds.
It should be noted that it is not necessary to initialize the object
from a literal. You can also use a function. For example:
\begin{mylstlisting}
X : String := Ada.Command_Line.Argument (1);
\end{mylstlisting}
This statement reads the first command-line argument and assigns it to X.
\subsection{Named subtype}
A subtype which has a name assigned to it. These subtypes are created
with the keyword type (remember that types are always anonymous, the
name in a type declaration is the name of the first subtype) or
subtype. For example:
\begin{mylstlisting}
type Count_To_Ten is range 1 .. 10;
\end{mylstlisting}
Count\_to\_Ten is the first subtype of a suitable integer base type.
If you however would like to use this as an index constraint on
String, the following declaration is illegal:
\begin{mylstlisting}
subtype Ten_Characters is String (Count_to_Ten);
\end{mylstlisting}
This is because String has Positive as index, which is a subtype of
Integer (these declarations are taken from package Standard):
\begin{mylstlisting}
subtype Positive is Integer range 1 .. Integer'Last;
type String is (Positive range <>) of Character;
\end{mylstlisting}
So you have to use the following declarations:
\begin{mylstlisting}
subtype Count_To_Ten is Integer range 1 .. 10;
subtype Ten_Characters is String (Count_to_Ten);
\end{mylstlisting}
Now Ten\_Characters is the name of that subtype of String which is
constrained to Count\_To\_Ten. You see that posing constraints on
types versus subtypes has very different effects.
\subsection{Unconstrained subtype}
A subtype of an indefinite subtype that does not add a constraint only
introduces a new name for the original subtype.
\begin{mylstlisting}
subtype My_String is String;
\end{mylstlisting}
\lstinline|My_String| and \lstinline|String| are interchangeable.
\section{Qualified expressions}
In most cases, the compiler is able to infer the type of an
expression; for example:
\begin{mylstlisting}
type Enum is (A, B, C);
E : Enum := A;
\end{mylstlisting}
Here the compiler knows that \lstinline{A} is a value of the type Enum.
But consider:
\begin{mylstlisting}
procedure Bad is
type Enum_1 is (A, B, C);
procedure P (E : in Enum_1) is... -- omitted
type Enum_2 is (A, X, Y, Z);
procedure P (E : in Enum_2) is... -- omitted
begin
P (A); -- illegal: ambiguous
end Bad;
\end{mylstlisting}
The compiler cannot choose between the two versions of P; both would
be equally valid. To remove the ambiguity, you use a ``qualified
expression'':
\begin{mylstlisting}
P (Enum_1'(A)); OK
\end{mylstlisting}
As seen in the following example, this syntax if often used when
creating new objects. If you try to compile the example, it will fail
with a compilation error since the compiler will determine that 256 is
not in range of Byte.
\mylstinputlisting{code/convert_evaluate_as.adb}
\section{Type conversions}
Data does not always come in the format you need them and you face the
task of converting them. Ada as a true multi-purpose language with a
special emphasis on "mission critical", "system programming" and
"safety" has several conversion techniques. As the most difficult
part is choosing the right one, I have sorted them so that you should
try the first one first; the last technique is "the last resort if all
other fails". Also added are a few related techniques which you might
choose instead of actually converting the data.
Since the most important aspect is not the result of a successful
conversion, but how the system will react to an invalid conversion,
all examples also demonstrate '''faulty''' conversions.
\subsection{Explicit type conversion}
An explicit type conversion looks much like a function call; it does
not use the ''tick'' (apostrophe, ') like the qualified expression
does.
\begin{mylstlisting}
Type_Name (''Expression'')
\end{mylstlisting}
The compiler first checks that the conversion is legal, and if it is,
it inserts a run-time check at the point of the conversion; hence the
name ''checked conversion''. If the conversion fails, the program
raises \lstinline|Constraint_Error|. Most compilers are very smart
and optimise away the constraint checks; so, you need not worry about
any performance penalty. Some compilers can also warn that a
constraint check will always fail (and optimise the check with an
unconditional raise).
Explicit type conversions are legal:
\begin{itemize}
\item between any two numeric types
\item between any two subtypes of the same type
\item between any two types derived from the same type
\item and ''nowhere else''
\end{itemize}
\begin{mylstlisting}
I: Integer := Integer (10); --Unnecessary explicit type conversion
J: Integer := 10; --Implicit conversion from universal integer
K: Integer := Integer'(10); --Use the value 10 of type Integer: qualified expression
--(qualification not necessary here).
\end{mylstlisting}
This example illustrates explicit type conversions:
\mylstinputlisting{code/convert_checked.adb}
Explicit conversions are possible between any two numeric types:
integers, fixed-point and floating-point types. If one of the types
involved is a fixed-point or floating-point type, the compiler not
only checks for the range constraints, but also performs any loss of
precision necessary.
Example 1: the loss of precision causes the procedure to only ever
print "0" or "1", since \lstinline{P / 100} is an integer and is always
zero or one.
\begin{mylstlisting}
with Ada.Text_IO;
procedure Naive_Explicit_Conversion is
type Proportion is digits 4 range 0.0 .. 1.0;
type Percentage is range 0 .. 100;
function To_Proportion (P : in Percentage) return Proportion is
begin
return Proportion (P / 100);
end To_Proportion;
begin
Ada.Text_IO.Put_Line (Proportion'Image (To_Proportion (27)));
end Naive_Explicit_Conversion;
\end{mylstlisting}
Example 2: we use an intermediate floating-point type to guarantee the
precision.
\begin{mylstlisting}
with Ada.Text_IO;
procedure Explicit_Conversion is
type Proportion is digits 4 range 0.0 .. 1.0;
type Percentage is range 0 .. 100;
function To_Proportion (P : in Percentage) return Proportion is
type Prop is digits 4 range 0.0 .. 100.0;
begin
return Proportion (Prop (P) / 100.0);
end To_Proportion;
begin
Ada.Text_IO.Put_Line (Proportion'Image (To_Proportion (27)));
end Explicit_Conversion;
\end{mylstlisting}
\subsection{Checked conversion for non-numeric types}
The examples above all revolved around conversions between numeric
types; it is possible to convert between any two numeric types in this
way. But what happens between non-numeric types, e.g. between array
types or record types? The answer is two-fold:
\begin{itemize}
\item you can convert explicitly between a type and types derived from
it, or between types derived from the same type,
\item and that's all. No other conversions are possible.
\end{itemize}
Why would you want to derive a record type from another record type?
Because of representation clauses. Here we enter the realm of
low-level systems programming, which is not for the faint of heart,
nor is it useful for desktop applications. So hold on tight, and
let's dive in.
Suppose you have a record type which uses the default, efficient
representation. Now you want to write this record to a device, which
uses a special record format. This special representation is more
compact (uses fewer bits), but is grossly inefficient. You want to
have a layered programming interface: the upper layer, intended for
applications, uses the efficient representation. The lower layer is a
device driver that accesses the hardware directly and uses the
inefficient representation.
\begin{mylstlisting}
package Device_Driver is
type Size_Type is range 0 .. 64;
type Register is record
A, B : Boolean;
Size : Size_Type;
end record;
procedure Read (R : out Register);
procedure Write (R : in Register);
end Device_Driver;
\end{mylstlisting}
The compiler chooses a default, efficient representation for
\lstinline{Register}. For example, on a 32-bit machine, it would probably
use three 32-bit words, one for A, one for B and one for Size. This
efficient representation is good for applications, but at one point we
want to convert the entire record to just 8 bits, because that's what
our hardware requires.
\begin{mylstlisting}
package body Device_Driver is
type Hardware_Register is new Register; -- Derived type.
for Hardware_Register use record
A at 0 range 0 .. 0;
B at 0 range 1 .. 1;
Size at 0 range 2 .. 7;
end record;
function Get return Hardware_Register; -- Body omitted
procedure Put (H : in Hardware_Register); -- Body omitted
procedure Read (R : out Register) is
H : Hardware_Register := Get;
begin
R := Register (H); -- Explicit conversion.
end Read;
procedure Write (R : in Register) is
begin
Put (Hardware_Register (R)); -- Explicit conversion.
end Write;
end Device_Driver;
\end{mylstlisting}
In the above example, the package body declares a derived type with
the inefficient, but compact representation, and converts to and from
it.
This illustrates that \emph{type conversions can result in a change of
representation}.
\subsection{View conversion, in object-oriented programming}
Within object-oriented programming you have to distinguish between
specific types and class-wide types.
With specific types, only conversions to ancestors are possible and,
of course, are checked. During the conversion, you do not "drop" any
components that are present in the derived type and not in the parent
type; these components are still present, you just don't see them
anymore. This is called a ''view conversion''.
There are no conversions to derived types (where would you get
the further components from?); \emph{extension aggregates} have
to be used instead.
\begin{mylstlisting}
type Parent_Type is tagged null record;
type Child_Type is new Parent_Type with null record;
Child_Instance : Child_Type;
-- View conversion from the child type to the parent type:
Parent_View : Parent_Type := Parent_Type (Child_Instance);
\end{mylstlisting}
Since, in object-oriented programming, an object of child type
\emph{is an} object of the parent type, no run-time check is
necessary.
With class-wide types, conversions to ancestor and child types are
possible and are checked as well. These conversions are also view
conversions, no data is created or lost.
\begin{mylstlisting}
procedure P (Parent_View : Parent_Type'Class) is
-- View conversion to the child type:
One : Child_Type := Child_Type (Parent_View);
-- View conversion to the class-wide child type:
Two : Child_Type'Class := Child_Type'Class (Parent_View);
\end{mylstlisting}
This view conversion involves a run-time check to see if
\lstinline{Parent_View} is indeed a view of an object of type
\lstinline{Child_Type}. In the second case, the run-time check accepts
objects of type \lstinline{Child_Type} but also any type derived
from \lstinline{Child_Type}.
\subsubsection{View renaming}
A renaming declaration does not create any new object and performs no
conversion; it only gives a new name to something that already exists.
Performance is optimal since the renaming is completely done at
compile time. We mention it here because it is a common idiom in
object oriented programming to rename the result of a view conversion.
\begin{mylstlisting}
type Parent_Type is tagged null record;
type Child_Type is new Parent_Type with null record;
Child_Instance : Child_Type;
Parent_View : Parent_Type'Class renames Parent_Type'Class (Child_Instance);
\end{mylstlisting}
Now, \lstinline{Parent_View} is not a new object, but another name for
\lstinline{Child_Instance}.
\subsection{Address conversion}
Ada's access type is not just a memory location (a thin pointer).
Depending on implementation and the access type used, the access might
keep additional information (a fat pointer). For example GNAT keeps
two memory addresses for each access to an indefinite object --- one
for the data and one for the constraint informations (Size, First,Last).
If you want to convert an access to a simple memory location you can
use the package Address\_To\_Access\_Conversions. Note however that
an address and a fat pointer cannot be converted reversibly into one
another.
The address of an array object is the address of its first component.
Thus the bounds get lost in such a conversion.
\begin{mylstlisting}
type My_Array is array (Positive range <>) of Something;
A: My_Array (50 .. 100);
A'Address = A(A'First)'Address
\end{mylstlisting}
\subsection{Unchecked conversion}
One of the great criticisms of Pascal was ``there is no escape''. The reason was that sometimes you have to convert the incompatible. For this purpose, Ada has the generic function \lstinline|Unchecked_Conversion|:
\begin{mylstlisting}
generic
type Source (<>) is limited private;
type Target (<>) is limited private;
function Ada.Unchecked_Conversion (S : Source) return Target;
\end{mylstlisting}
\lstinline|Unchecked_Conversion| will bit-copy the data and there are
absolutely no checks. It is \emph{your} chore to make sure that the
requirements on unchecked conversion as stated in RM 13.9
\footnote{http://www.adaic.com/standards/95lrm/html/RM-13-9.html} are
fulfilled; if not, the result is implementation dependent and may even
lead to abnormal data.
A function call to (an instance of) \lstinline|Unchecked_Conversion| will
copy the source to the destination. The compiler may also do a
conversion in place (every instance has the convention intrinsic).
To use \lstinline|Unchecked_Conversion| you need to instantiate the generic.
In the example below, you can see how this is done. When run, the
example it will output ``A = -1, B = 255''. No error will be reported,
but is this the result you expect?
\mylstinputlisting{code/convert_unchecked.adb}
\subsection{Overlays}
If the copying of the result of \lstinline|Unchecked_Conversion| is too
much waste in terms of performance, then you can try overlays, i.e.
address mappings. By using overlays, both objects share the same
memory location. If you assign a value to one, the other changes as
well. The syntax is:
\begin{mylstlisting}
for Target'Address use expression;
pragma Import (Ada, Target);
\end{mylstlisting}
where \lstinline|expression| defines the address of the source object.
While overlays might look more elegant than
\lstinline|Unchecked_Conversion|, you should be aware that they are
even more dangerous and have even greater potential for doing
something very wrong. For example if \lstinline{Source'Size < Target'Size} and you assign a value to Target, you might
inadvertently write into memory allocated to a different object.
You have to take care also of implicit initialisations of objects of
the target type, since they would overwrite the actual value of the
source object. The Import pragma with convention Ada can be used to
prevent this, since it avoids the implicit initialisation, RM B.1
\footnote{http://www.adaic.com/standards/95lrm/html/RM-B-1.html}.
The example below does the same as the example from ``Unchecked Conversion''.
\mylstinputlisting{code/convert_address_mapping.adb}
\subsection{Export / Import}
Just for the record: There is still another method using the
\lstinline{Export} and \lstinline{Import} pragmas. However, since this
method completely undermines Ada's visibility and type concepts even
more than overlays, it has no place here in this language introduction
and is left to experts.
\section{See also }
\subsection{Ada Reference Manual}
\begin{itemize}
\item Type Declarations \footnote{http://www.adaic.com/standards/95lrm/html/RM-3-2-1.html}
\item Objects and Named Numbers \footnote{http://www.adaic.com/standards/95lrm/html/RM-3-3.html}
\item Discriminants \footnote{http://www.adaic.com/standards/95lrm/html/RM-3-7.html}
\item Access Types \footnote{http://www.adaic.com/standards/95lrm/html/RM-3-10.html}
\item Static Expressions and Static Subtypes \footnote{http://www.adaic.com/standards/95lrm/html/RM-4-9.html}
\item Unchecked Type Conversions \footnote{http://www.adaic.com/standards/95lrm/html/RM-13-9.html}
\item Operational and Representation Attributes \footnote{http://www.adaic.com/standards/95lrm/html/RM-13-3.html}
\item Annex K (informative) Language-Defined Attributes \footnote{http://www.adaic.com/standards/95lrm/html/RM-K.html}
\end{itemize}
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.