CS141 Lecture 17
Variant Records
What are Variant Records?
Variant RECORD TYPEs are RECORD TYPEs that
support multiple
record structures within a single record type.
Example: A geometric object record that has different components
depending on whether the object is a rectangle (height and width)
versus a circle (radius).
Picture:

Note: The size of the record can
change between
variants!
How much memory should the compiler allocate for a variant record:
- The maximum amount needed to hold
any variant?
- This can waste a lot of space
if there is a rarely
used abnormally large variant.
- Just the amount needed to hold a
particular variant?
- Now you have to be careful
about switching between
variants, since the needed space may not be available.
Answer: It depends!
When should you use Variant Records?
Answer: Rarely!
Most cases can be handled by Ada TAGGED TYPEs (same as
classes and
subclasses in C++ and Java) with less pain and a lot more flexibility.
(In C and C++ Variant Records correspond to Unions. There is no
corresponding capability in Java.)
Also, in Ada, you cannot use Variant Records to switch between
different views of the same set of bits (a standard use for Unions in C
and C++). In Ada use Unchecked_Conversion if you really(!) need to do
this.
Historical Note: Variant RECORD TYPEs was an important feature of
Ada83, which did not support TAGGED TYPEs. With the addition of TAGGED
TYPEs in Ada95, Variant Records are mostly used in conjunction with old
Ada code.
Defining a Variant Record
TYPE type_id( discriminant_id : discriminant_type := default_value )
IS RECORD
Id1 : Type1;
...
fixed part of record
...
Idn : Typen;
CASE discriminant_id
IS
WHEN
value1
=>
fields for value1
WHEN
value2
=>
fields for value2
...
variant
part
...
WHEN
valuen
=>
fields for valuen
WHEN
OTHERS =>
fields for all other cases
END CASE;
END RECORD;
Example:
TYPE
Geom_Type IS (Unknown,
Rectangle, Circle);
TYPE
Geometric_Figure(Figure_Type : Geom_Type := Unknown) IS RECORD
-- Invariant data fields here
CASE
Figure_Type IS
WHEN
Rectangle =>
Width : Float;
Height : Float;
WHEN
Circle =>
Radius : Float;
WHEN
Unknown =>
NULL;
END CASE;
END RECORD;
Notes:
- the discriminant type is (of
course) a discrete type
- the default_value is optional
- However, it is recommended, see
discussion on
constrained and unconstrained record types, below.
- WHEN OTHERS is optional. It is
only needed when the list of possible cases has not already been
exhausted by the other WHEN value clauses.
- the discriminant_id, the field
names in the fixed part, and the field names in the variant parts must
all be unique
- Two distinct variant parts
cannot use the same field
name. (Bah Humbug!)
- An empty list of fields is indicated
using the NULL
keyword
Constrained and Unconstrained Variant Records
A Variant record object is Unconstrained if the
variant can
change (after the initial declaration). A Variant record object in
Constrained if the variant can not be changed (after the initial
declaration).
Memory allocation:
- An Unconstrained object will have
the memory required
to handle any variant
- E.g. An unconstrained
Geometric_Figure object will
have enough memory available to hold a Rectangle
- A Constrained object will have
only the memory needed
to handle that particular variant
- E.g. An constrained circle
Geometric_Figure:
Geometric_Figure(Figure_Type => Circle) will have exactly the amount
of memory needed to hold a Circle figure
Example:
GF : Geometric_Figure;
-- An unconstrained
Geometric_Figure
GC : Geometric_Figure( Figure_Type => Circle );
-- A constrained
Geometric Figure of Figure_Type Circle
Note: An Unconstrained Variant Record object has its
discriminant
initialized to the default value specified in the declaration. Thus, if
there is no default value for the discriminant, you cannot declare an
unconstrained object.
Ada always guarantees:
- The discriminant always has a value.
- For constrained objects it is
the value when the
object was created.
- For unconstrained objects it is
initially the default
value
- This value can be
subsequently changed
- The
discriminant value is always consistent with the
actual data stored in the record.
Operations on Variant Records
- Retrieving field values:
- You can always retrieve the
value of the discriminant
- You can always retrieve the
values of fields in the
fixed part
- You can only retrieve the
values of fields in the
variant part when the value of the discriminant matches the chosen
variant
- Storing field values:
- You can only store the value of
the discriminant if:
- You don't change the value, or
- As part of a record assignment
- You can always store the values
of fields in the
fixed part
- You can only store the values
of fields in the
variant part when the value of the discriminant matches the chosen
variant
- Record assignment:
- A variant record value can
always be assigned to an
unconstrained variant variable of the correct record type
- A variant record value can only
be assigned to a
constrained variant variable (of the correct record type) if the
discriminants match
- Record comparison:
- You can only compare variant record
types when the
variants agree (Bah Humbug!)
Geometric Figures Example