Created
April 10, 2016 00:23
-
-
Save atweiden/abacb9632d7cdbddf9a3999aec33073e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* vim: ft=journal */ | |
Ada Tutorial | |
============ | |
Variables and Types | |
------------------- | |
- Variables must be declared between a procedure/function's `is` and `begin` | |
statements | |
- To create a read-only variable you may either: | |
1. use the `constant` reserve word in variable declaration | |
- `Input : constant Months := Months'Value (Ada.Command_Line.Argument (1));` | |
2. implement a function that takes no parameters | |
- “Notice that Ada does not require (or even allow) you to use | |
an empty parameter list on functions that take no parameters. This | |
means that function `End_Of_File` looks like a variable when it | |
is used. This is considered a feature; it means that read-only | |
variables can be replaced by parameterless functions without | |
requiring any modification of the source code that uses that | |
variable.” | |
- Ada is strongly typed. There is no type inference. | |
- The `Natural` type: | |
- `subtype Natural is Integer range 0 .. Integer'Last;` | |
- The `Positive` type: | |
- `subtype Positive is Integer range 1 .. Integer'Last;` | |
Case Statements | |
--------------- | |
- must be exhaustive | |
- must not contain clauses that overlap | |
Package Imports | |
--------------- | |
- standard: | |
```ada | |
with Compress.Algo.LZW; | |
procedure Hello is | |
Compressor : Compress.Algo.LZW.LZW_Engine; | |
begin | |
Compress.Algo.LZW.Process(Compressor, "Compress Me!"); | |
end Hello; | |
``` | |
- aliasing with `renames`: | |
```ada | |
with Compress.Algo.LZW; | |
procedure Hello is | |
package Squeeze renames Compress.Algo; | |
Compressor : Squeeze.LZW.LZW_Engine; | |
begin | |
Squeeze.LZW.Process(Compressor "Compress Me!"); | |
end Hello; | |
``` | |
- "radical" aliasing example with `subtype` and `renames`: | |
```ada | |
with Compress.Algo.LZW; | |
procedure Hello is | |
subtype LZW_Type is Compress.Algo.LZW.LZW_Engine; | |
procedure Comp(Engine : LZW_Type; Data : String) | |
renames Compress.Algo.LZW.Process; | |
Compressor : LZW_Type; | |
begin | |
Comp(Compressor "Compress Me!"); | |
end Hello; | |
``` | |
- `use` statement: | |
```ada | |
with Compress.Algo.LZW; use Compress.Algo.LZW; | |
procedure Hello is | |
Compressor : LZW_Engine; | |
begin | |
Process(Compressor, "Compress Me!"); | |
end Hello; | |
``` | |
- `use type` statement: | |
```ada | |
procedure Example is | |
use type Big_Number.Number_Type; | |
X, Y, Z : Big_Number.Number_Type; | |
begin | |
-- Fill Y and Z with interesting values . | |
X : = Y / Z; | |
exception | |
when Big_Number.Divide_By_Zero => | |
Put_Line("Big Number division by zero!"); | |
when others => | |
Put_Line("Unexpected exception!"); | |
end Example; | |
``` | |
- “The previous example also shows an interesting detail related | |
to operator overloading. The example assumes that there is | |
a context clause of `with Big_Number` (not shown) but no | |
`use` statement. Thus the division operator is properly named | |
`Big_Number."/"`. Unfortunately it can’t be called using the | |
infix operator notation with that name. There are several ways | |
to get around this. One could include a `use Big_Number` in the | |
context clause or in the procedure’s declarative part. However | |
that would also make all the other names in package `Big_Number` | |
directly visible and that might not be desired. An alternative is | |
to introduce a local function named `/` that is just a renaming | |
(essentially an alias) of the function in the other package. As we | |
have seen Ada allows such renaming declarations in many situations, | |
but in this case, every operator function would need a corresponding | |
renaming and that could be tedious. Instead the example shows a more | |
elegant approach. The `use type` declaration tells the compiler that | |
the primitive operations of the named type should be made directly | |
visible. I will discuss primitive operations in more detail in the | |
section on object oriented programming. In this case, the operator | |
overloads that are declared in package `Big_Number` are primitive | |
operations. This method allows all the operator overloads to be | |
made directly visible with one easy statement, and yet does not make | |
every name in the package directly visible.” | |
Error Types | |
----------- | |
- `Constraint_Error` | |
- Raised whenever a constraint is violated. This includes going outside | |
the bounds of a subtype (or equivalently the allowed range of an | |
array index) as well as various other situations. | |
- `Program_Error` | |
- Raised when certain ill-formed programs that can’t be detected | |
by the compiler are executed. For example, if a function ends without | |
executing a return statement, `Program_Error` is raised. | |
- `Storage_Error` | |
- Raised when the program is out of memory. This can occur during | |
dynamic memory allocation, or be due to a lack of stack space when | |
invoking a subprogram. This can also occur during the elaboration | |
of a declarative part (for example if the dynamic bounds on an array | |
are too large). | |
- `Tasking_Error` | |
- Raised in connection with certain tasking problems. | |
Access Types | |
------------ | |
- Access types can be named or anonymous. I will only consider named | |
access types; anonymous access types have special properties that are | |
outside the scope of this tutorial. For example the declaration: | |
`type Integer_Access is access Integer;` declares `Integer_Access` | |
as a type suitable for accessing, or pointing at, integers. Once the | |
access type has been declared, variables of that type can then be | |
declared in the usual way. | |
- Ada automatically initializes access variables to the special value | |
`null` if no other initialization is given. Thus access variables are | |
either `null` or they point at some real object. Indeed, the rules of | |
Ada are such that, under appropriate circumstances, dangling pointers | |
are not possible. Access variables can be copied and compared like any | |
other variable. For example if `P1` and `P2` are access variables than | |
`P1 = P2` is true if they both point at the same object. To refer to | |
the object pointed at by an access variable you must use the special | |
`.all` operation: `P.all := 1;`. | |
- The `.all` syntax plays the same role in Ada as the indirection operator | |
(the `*`) plays in C and C++. The use of `.all` may seem a little odd | |
in this context, but understand that most of the time access types | |
are pointers to aggregate entities such as arrays or records. In | |
that case, the components of the array or record pointed at can be | |
accessed directly by using the component selection operation on the | |
access variable itself. This is shown as follows: | |
```ada | |
type Date is | |
record | |
Day, Month, Year : Integer; | |
end record; | |
type Date_Access is access Date; | |
D1, D2 : Date_Access; | |
… | |
D1.Day := 1; -- Accesses the Day member of referenced Date. | |
D1 := D2; -- Causes D1 to point at same object as D2. | |
D1.all := D2.all; -- Copies Date objects. | |
``` | |
- In this case the `.all` syntax is very natural. It shows that you are | |
accessing all the members of the referenced object at the same | |
time. It is important to notice that Ada normally “forwards” | |
operations applied to the access variable to the referenced object. | |
Thus `D1.Day` in the example means the `Day` component of the object | |
pointed at by `D1`. Only operations that are meaningful for the | |
access type itself are not forwarded. Thus `D1 := D2` copies the | |
access values. To copy the objects pointed at by the access variables | |
one must use the `.all` syntax. I should also note that syntax such as | |
`D1.all.Day`, while verbose, is also legal. The `.all` dereferences | |
`D1`. The result is a date record so the selection of the `Day` | |
component is meaningful. | |
- Access variables that refer to arrays also allow the normal array | |
operations to be forwarded to the referenced object as the following | |
example illustrates: | |
```ada | |
type Buffer_Type is array (0..1023) of Character; | |
type Buffer_Access is access Buffer_Type; | |
B1, B2 : Buffer_Access; | |
… | |
B1 := B2; -- Copies only the access values. | |
B1. all := B2.all; -- Copies arrays. | |
B1(0) := 'X'; -- Forwards index operation. | |
for I in B1'Range loop -- Forwards 'Range attribute. | |
… | |
end loop; | |
``` | |
- Because of forwarding, using access types is generally quite convenient | |
in Ada. However, you must keep in mind that operations that are | |
meaningful for the access types themselves will be applied directly | |
to the access variable and are not forwarded. | |
- So far we’ve seen how to declare and use access types. How does one | |
get an access variable to point at another object in the first place? In | |
Ada 83 there was only one way: using the `new` operation. For example: | |
`P := new Integer'(0);` | |
dynamically allocates an integer, initializes that integer to zero, | |
and then assigns the resulting access value to `P`. Here I assume `P` | |
has an access to integer type. Notice that the argument to `new` has | |
the form of a qualified expression (the apostrophe is required). Also | |
as with dynamically allocated objects in other languages, the lifetime | |
of the object created in this way extends beyond the lifetime of the | |
access variable used to point at it. | |
Garbage Collection | |
------------------ | |
- Ada allows, but does not require garbage collection. Thus for maximum | |
portability one must assume that garbage collection is not done and | |
take steps accordingly. | |
- The Ada library provides a generic procedure named | |
`Unchecked_Deallocation` that can be used to manually deallocate a | |
dynamically allocated object. Unfortunately the use of | |
`Unchecked_Deallocation` can violate important safety properties the | |
language otherwise provides. In particular, if you deallocate an object | |
while an access variable still points at it, any further use of that | |
access variable will result in erroneous behavior. As a service | |
`Unchecked_Deallocation` will set the access variable you give it to | |
`null` causing future use of that access variable to result in a well | |
defined exception. However, there might be other access variables | |
that point at the same object and `Unchecked_Deallocation` cannot, | |
in general, know about all of them. | |
- If your program never uses `Unchecked_Deallocation` then all access | |
variables are either `null` or point at a real object; dangling pointers | |
are impossible. However, your program might also leak memory if the Ada | |
implementation does not provide garbage collection. Thus in most real | |
programs `Unchecked_Deallocation` is used. | |
- This is an example of where Ada compromises safety for the sake of | |
practical reality. In fact, there are several other “Unchecked” | |
operations in Ada that are used to address certain practical concerns | |
and yet introduce the possibility of unsafe programs. Since every such | |
operation starts with the word “Unchecked” it is an simple matter | |
to search an Ada program for occurrences of them. This feature makes | |
reviewing the unchecked operations easier. | |
************************************************************************ | |
- Credit: https://github.com/pchapin/tutorialada |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment