


                                                       Chapter 20
                                           ADVANCED RECORD TOPICS


A RECORD WITH A DISCRIMINANT
_________________________________________________________________

Examine the file named DISCRIM1.ADA for our      ================
first example of a record with a discriminant.     DISCRIM1.ADA
It will take a little time and study before we   ================
get to the discriminant and what it does, but we
will take it a step at a time.

We begin by defining an unconstrained two dimensional array type
in line 10 of the declaration part, and an unconstrained one
dimensional array type in line 13.  Next we define a record type,
beginning in line 15, with a discriminant, the discriminant being
a variable named List_Size which is of type POSITIVE.  The record
is composed of four fields, each of which is defined in part by the
discriminant.  The variable named Matrix is a square array whose
size is given by the value of the discriminant, while Elements is
initialized to the square of the discriminant.  Likewise, the other
two fields are defined as a function of the discriminant.  Keep in
mind that the discriminant still does not have a value, it is only
used as a part of the pattern of the record.

For later use, we define a derived type in line 23, and a subtype
in line 25.  The subtype is defined as being of type STUFF but with
the discriminant being fixed at 5.  We will have more to say about
these two types later.



WE NEED TO DEFINE SOME DATA NOW
_________________________________________________________________

In line 27, we declare a variable named Data_Store to be of type
STUFF with the discriminant set equal to 5.  Therefore, the Matrix
variable which is a part of the record named Data_Store will have
two subscripts, each of which covers a range of 1 through 5.  The
variable named Elements will be initialized to the square of 5, and
the other fields will likewise be defined.  The variable Big_Store
will have larger arrays, and the value of its subfield, named
Elements, will be initialized to a value of the square of 12.
Since these two variables have different numbers of elements, they
are not assignment compatible, nor can they be compared for
equality or inequality.

The variable Extra_Store is declared as being of type ANOTHER_STUFF
with a discriminant of 5, but since the types are different, this
variable is not assignment compatible with the first variable named
Data_Store.  More_Store is declared as being of type STUFF with a
discriminant of 5, so it is assignment compatible with Data_Store.

                                                        Page 20-1

                              Chapter 20 - Advanced Record Topics

Five_Store, because it is a subtype of STUFF, and its discriminant
is 5, as defined in the subtype declaration, is assignment
compatible with Data_Store.  Finally, it should be clear that the
last example, Name_Store, is assignment compatible with Data_Store,
since its only difference is that it uses the named method of
discriminant selection.  This is a hint to you that additional
discriminants can be used, and there is actually no limit to the
number that can be a part of a discriminated record type.  We will
have an example program soon that has three discriminants.



WHO IS ASSIGNMENT COMPATIBLE WITH WHO?
_________________________________________________________________

As mentioned before, the variables named Data_Store, More_Store,
Five_Store, and Name_Store, are all of the same type and can be
freely assigned to each other.  They can also be compared for
equality or inequality with each other.  The other variables are
of different types and cannot be assigned as a complete record to
each other, or compared for equality or inequality.

The discriminant, once declared, is considered to be a constant,
and cannot be modified.  The discriminant of the variable
Data_Store is accessed in the program as Data_Store.List_Size for
purposes of reading it.  The executable part of the program should
be clear.  One of the declared Matrix variables is assigned values
using the RANGE attribute for the loop limits.  The entire record
is then assigned to some additional record variables, and a single
data point is displayed as an example.

When you understand the program, compile and execute it to prove
to yourself that it works as shown.  Notice that, as always, the
elements of the record cannot be anonymous types but must be named.



HOW DO WE USE THE NEW RECORDS?
_________________________________________________________________

Examine the example program named DISCRIM2.ADA   ================
for a few examples of how to use the               DISCRIM2.ADA
discriminated record.  The type declarations are ================
identical to the last program, but only two
records are declared this time, Data_Store and
Big_Store, which are of different types because they have different
discriminants.

The declaration part of the program has a function declaration and
a procedure declaration added to it in this program.  If you look
closely, you will see that the type used for the formal variable
in both subprograms is of the record type STUFF, but there is no
discriminant defined for either.  These are unconstrained records
and add flexibility to the use of subprograms.  The loops within

                                                        Page 20-2

                              Chapter 20 - Advanced Record Topics

the subprograms use limits that are dependent upon the limits of
the actual type as defined in the calling program, and the
subprograms can therefore be used for any record variable of type
STUFF regardless of the value of the discriminant.



USE OF THE UNCONSTRAINED SUBPROGRAMS
_________________________________________________________________

The nested loop in lines 48 through 53, assigns the elements
contained in the array variable Data_Store.Matrix to a
multiplication table for later use.  In line 55, we call the
procedure Set_To_Ones with the record Big_Store to set all of its
Matrix values to 1.  Finally, we display the sums of all of the
elements by calling the function Add_Elements once for each record.
Even though the records are actually of different types, the
function works correctly with both, because of the flexibility
built into the function itself.  Note that even though the record
is unconstrained in each subprogram, it is constrained when the
subprogram is called since the discriminant is constrained to the
value of the actual parameter in the call.

Be sure to compile and execute this program and study the output
until you are sure you understand the results.



A VARIABLE DISCRIMINANT
_________________________________________________________________

Examine the program named DISCRIM3.ADA for an    ================
example of a discriminant that can be changed      DISCRIM3.ADA
dynamically.  This program is nearly identical   ================
to the last example program, but there are two
very small changes.  The discriminant is
initialized to a value of 2 in line 15, which will be used for the
discriminant if none is given in the variable declaration. This is
illustrated in line 25, where the variable Var_Store is declared
to be of type STUFF, and is defaulted to the initialization value
of 2 for its discriminant.  There is one other property that it has
acquired, and that is the ability to have its discriminant changed
to any legal value during execution of the program.

The two variables named Data_Store and Big_Store have their
discriminants fixed at 5 and 12 respectively and cannot be changed
during program execution.


HOW DO WE CHANGE THE DISCRIMINANT?
_________________________________________________________________

The discriminant can only be changed by changing the entire record
in a single statement as is illustrated in line 56 where the entire

                                                        Page 20-3

                              Chapter 20 - Advanced Record Topics

record named Data_Store, with a discriminant of 5, is assigned to
the variable Var_Store.  The variable Var_Store can then be used
anywhere it is legal to use a record of discriminant 5 as shown in
lines 57 through 63.  Note that prior to the assignment in line 56
the variable Var_Store can be used as a record with its
discriminant set to 2 as the default.

In line 67, the variable Var_Store is assigned the entire record
of Big_Store which effectively changes it into a record variable
with a discriminant of 12.  The fact that it is changed is
evidenced by the output which you can see after you compile and
execute this program.  Note that all of the values contained in
Big_Store are copied into Var_Store.  Be sure to compile and
execute this program.



A MULTIPLE DISCRIMINANT
_________________________________________________________________

Examining the example program named DISCRIM4.ADA ================
gives you an example of how to use a multiple      DISCRIM4.ADA
discriminant in a record type.  The first        ================
difference is in lines 15 through 17 where three
discriminants are defined for the record type,
and the next big difference is in lines 25 through 31 where 5
variables are declared with this type illustrating the various
kinds of discriminant initialization described in this chapter.

Since we have studied all of these in the last few example
programs, we will not elaborate on them, except to mention that the
variable named Variable can be assigned the value of any of the
other variables.  It will then have the entire set of discriminants
assigned to it that the assignment variable possesses and it will
be assigned all of the current values stored in the source record.
This is exactly the same as what was illustrated in the last
program.

Be sure to compile and run this program after you understand the
concepts it is meant to convey to you.



A VARIANT RECORD
_________________________________________________________________

Examine the file named VARIANT1.ADA for our      ================
first example of a variant record.  A variant      VARIANT1.ADA
record must have a discriminant, by definition,  ================
since the discriminant will define which of the
various variants each record variable will be
composed of.  If you are a Pascal programmer you will find the
variant record to be much more confining than in Pascal where you
can change the variant at will.  If you remember that Ada is a very

                                                        Page 20-4

                              Chapter 20 - Advanced Record Topics

strongly typed language, and will accept no deviation from its
defined standard in order to detect errors inadvertently introduced
by the programmer, you will appreciate the terse definition and
restrictions.

The discriminant for our example record is declared in line 10 as
an enumerated type with four allowable values.  A variable named
Engine is declared, which is of type POWER, which will be used as
the discriminant and two variables are declared as the first part
of the record.  The variant part of the record begins in line 16
with the reserved word case followed by the name of the
discriminant variable, which is Engine in this example.  The four
variants are declared in much the same way as a normal case
statement with variable declarations in place of the executable
statements.  There must be a clause listed for each possible value
of the discriminant, and any number of variables may be defined for
each, including none.  If no variables are declared for a case, the
reserved word null is used to indicate to the compiler that you
really mean to include no variables there.

If there is a variant to the record, it must be the last part of
the record with all common variables declared first.  One or more
of the variant parts of the record can have a variant part itself,
provided it is the last part of the variant part.  There is no
defined limit to the nesting.



HOW DO WE USE THE VARIANT RECORD?
_________________________________________________________________

In line 25, we declare the variable Ford to be a record of type
VEHICLE, and constrain it to be the GAS variant of the record.
Because the Ford variable is constrained to the GAS variant, it can
only use the variable declared as part of the GAS variant, and of
course the two common variables.  It would be illegal to assign
data to the fields of the Ford variable which are declared in the
other variants of the record type.  Moreover, since the variable
Ford has been assigned the discriminant value of GAS, it can never
be changed, but is a constant.  Likewise, the variable named Truck
will always be a record of type VEHICLE with the variant DIESEL
because that is what it is declared with.  The same is true of the
other two variables declared in lines 27 and 28.  We will see in
the next program, that it is possible to declare a variable in such
a way that it can be modified dynamically to any of the variants
as the program is executing.



HOW DO WE ASSIGN VALUES TO THE VARIABLES?
_________________________________________________________________

Lines 32 through 34 should be familiar to you since this is the
method used to assign values to a record with no variant, which we

                                                        Page 20-5

                              Chapter 20 - Advanced Record Topics

studied earlier.  Line 36 illustrates value assignment by using a
positional aggregate notation, and line 39 illustrates assignment
of values using the named aggregate notation.  In both of these
cases, all four fields must be named even if some are not changed.
Even the invariant discriminant must be included in the aggregate,
which seems to be somewhat of a nuisance, since it is a constant.
The reasons for these last two rules are probably well founded and
have to do with ease of writing a compiler, which is certainly no
small job.

The statements in lines 42 through 45 assign values to each of the
subfields of the record variable named Stanley, and the mixed
aggregate assignment is illustrated in line 47, where all five
variables are mentioned even though the discriminant is a constant.
Finally, the Schwinn and Truck variables are assigned values in
lines 50 through 56.  Compile and execute this program to assure
yourself that your compiler will indeed compile it correctly.



A VARIABLE VARIANT RECORD
_________________________________________________________________

Examine the program named VARIANT2.ADA for an    ================
example of a variant record in which we can        VARIANT2.ADA
change the variant dynamically during program    ================
execution.  A major difference here is that the
discriminant is defaulted to the value listed in
line 12, namely the value of NONE.  If no variant is declared, it
is defaulted to NONE in the declarations as is done in line 25,
where three variable records are declared using the default value
of the discriminant.  The variable Stanley is once again declared
to be of the variant STEAM, and this will remain constant
throughout the execution of the program, because any variable
declared with a discriminant value cannot have the value of its
discriminant changed dynamically but is a constant, although the
individual elements of the record can be changed.



NOW TO USE SOME OF THE NEW VARIABLES
_________________________________________________________________

In line 30, the variable Ford is assigned data such that it is of
the GAS variant, using the positional aggregate notation, and is
redefined in the next statement to be of the DIESEL variant, by
using the mixed aggregate notation.  This is done to illustrate to
you that it is possible to change the variant of a variable if it
was declared with the default variant.  In line 33, the variable
Truck is assigned the DIESEL variant with the positional aggregate
notation, and two of the fields are changed in the next two
statements.



                                                        Page 20-6

                              Chapter 20 - Advanced Record Topics

Any of the fields can be changed individually with the exception
of the discriminant variable, which can only be changed by use of
an aggregate in which all values are listed.  Remember that this
is the aggravating part of this construct, that all of the fields
must be mentioned in every record aggregate.  Even though you can
change individual values of the record, you are limited to using
those variables that are part of the current variant.  Pascal
allows you to use variable names of other variants but Ada will not
permit this.  You are permitted to use the positional, named, or
mixed aggregate notation, however.  Be sure to compile and execute
this program after you understand the concepts.



OPERATOR OVERLOADING
_________________________________________________________________

The example program named INFIX.ADA could be      ===============
placed in several different places in this           INFIX.ADA
tutorial, because it doesn't really fit anywhere  ===============
too well.  Since one of the most advantageous
places to use operator overloading is with
record type variables, this seemed like a good place for it.  The
program itself is very simple, but the concept is potentially very
powerful.

We first define a record composed of three simple variables all
being of type INTEGER, and use this definition to declare three
variables of this type.  The next declaration is of a function
named "+", which we can define to do anything we wish it to do.
In this case we input two variables of the record type THREE_INTS
and return one record of the same type.  Within the function, we
add all three fields of one variable to the corresponding fields
of the other variable, and return the record consisting of the sum
of the two input records.  To make it even more convenient, Ada
allows you to use the overloaded operator in an infix notation as
illustrated in line number 33 of the program where two records are
added together and assigned to the record variable named Sum.  This
line is actually a call to the function we defined earlier and
named "+".

It could be a bit confusing if you consider the addition in line
27 where the usual addition operator is used.  Ada will decide
which + operator to use based on the type of the constants or
variables to be added together.  If you were to try to use this
operator on a record of some other type, such as the VEHICLE record
from the last program, the system would generate a type error
during compilation.  As mentioned previously, it is only possible
to overload existing operators.  You cannot define a new operator,
such as &=, and use it for some operation.

Recall the chapter named Advanced Array Topics where we overloaded
the "+" operator in the example program named ARRAYOP2.ADA.  Both
overloadings of this operator could be included in a single program

                                                        Page 20-7

                              Chapter 20 - Advanced Record Topics

and the system would be able to find the correct overloading for
each use by checking the types used.  Be sure to compile and
execute this program even though it has no output.



PROGRAMMING EXERCISES
_________________________________________________________________

1.   Add some output statements to VARIANT1.ADA to display some of
     the results.

2.   Try to change the variant of Stanley in the same way we
     changed the variant of Ford in the example program named
     VARIANT2.ADA to see what kind of error messages you get.

3.   Add another overloading function to INFIX.ADA which uses the
     "+" operator to add all six elements of two records together
     and return a single INTEGER type variable.  The system can
     tell which overloading you wish to use by the return type
     associated with the function call.
































                                                        Page 20-8