Reading Out Parameters in Ada83

There is a darned Ada83 rule forbidding reading formal out-parameters. You often find some contorted code to cope with this restriction - even in cases where it is absolutely unnecessary.

Suppose you have a bunch of documents (a discriminated record) you want to store. So you declare the following package:

package Archive is

  type Key_Template is (Desk, Safe, ...);

  type Document (Key: Key_Template := Desk) is record
    case Key is
      when Desk => Confidential: ...;
      when Safe => Secret      : ...;
      when ...
    end case;
  end record;

  procedure Store    (Letter: in Document);
  procedure Retrieve (<Parameters>);

end Archive;

package body Archive is

  Storage: array (Key_Template) of Document;

  procedure Store (Letter: in Document) is
    Storage (Letter.Key) := Letter;
  end Store;

  procedure Retrieve (<Parameters>) is
  end Retrieve;

end Archive;

Now our poor programmer is brooding about how to define the retrieve operation.

which has the wrong mode in out (it's a pure out parameter), or

which is unnecessarily tedious because of the first parameter use_Key. Now if you ask why all these contortions instead of the simple and correct and symmetric

you will undoubtedly and invariably get the answer: "But you can't read out-parameters."

Of course you can, at least some parts - of arrays, you may read the limits, of records, you may read the discriminants and the limits and discriminants ot their components, ARM_83 6.2 (5).

Why is this so? Well, how else should the following ever work?

Everyone knows Text_IO. Here Text is the actual parameter for the formal out-parameter Item, and in the body of Get_Line, something like the following happens:

The attributes Item'First [21], Item'Last [30], Item'Range may be used to read the limits [the values for our example are given in brackets].

Thus the solution for our key problem is

and we empty the safe like so:

When however nothing has ever before been stored in the safe, we get Constraint_Error. Have we forgotten something?

The Storage variable is uninitialised. Therefore for each entry the same variant (the default Desk) is stored and the discriminant check fails.

Now initialisation is apt to get very tedious as we shall see presently if not done in a smart way. First we add a component denoting whether there is stored anything at all.

The package body gets an executable part (executed during elaboration):

To our dismay, with this method in a way we have to repeat the whole record declaration. If we have many variants, this is getting really awkward.

Fortunately all this is unnecessary if we add the correct default values to the record declaration:

Initialising now is really simple.

Note the variable declaration without an initial value a constant would need an initial value and we were back at the problem how to denote it. (By the way: Declarations without initial value are strictly disallowed by some coding standards, especially when the variable is never assigned a value, as is the case here! But as we have seen, sometimes seemingly obvious nonesense is unavoidable and if done in the correct way perfectly legal.)

Contributed by: Christoph Karl Walter Grein
Contributed on: December 9, 1998
License: Public Domain