From: Nick Roberts <nick.roberts@acm.org>
Subject: Re: How to define an array of a variant record type?
Date: Tue, 18 Nov 2003 17:48:10 +0000
Date: 2003-11-18T17:48:10+00:00 [thread overview]
Message-ID: <bpdm15$1o7hc7$1@ID-25716.news.uni-berlin.de> (raw)
In-Reply-To: <BBDEBC6C.902D%office@anobject.net>
Harald Schmidt wrote:
> I would like to define an array, which its element type is a variant record
> and the Value_Type is only known at runtime.
>
> type Name_Value_Type is (String_Type,
> Integer_Type,
> Float_Type);
>
> type Name_Value(Value_Type : Name_Value_Type) is
> record
> The_Name : Unbounded_String;
> case Value_Type is
> when String_Type =>
> String_Value : Unbounded_String;
> when Integer_Type =>
> Integer_Value : Integer;
> when Float_Type =>
> The_Value : Float;
> end case;
> end record;
>
> type Name_Value_Sequence is array(Natural range <>) of Name_Value;
>
> The compiler says "unconstrained element type in array declaration",
> ...right!
>
> I know Ada is a strong typed language, but sometimes information is only at
> runtime available. Does anyone know some workaround, or a regulare design
> pattern for this problem?
So, to summarise the various answers to this question, you could use
indirection or default discriminants.
An example of using indirection:
type Name_Value_Ptr is access Name_Value;
type Name_Value_Sequence is array (Natural range <>) of Name_Value_Ptr;
Here Name_Value_Ptr is a pool-specific access type. You can create values
of the designated type (Name_Value) by allocating them (generally in an
area of memory called the 'heap'). For example:
function "+" (Right: String)
return Unbounded_String renames To_Unbounded_String;
X: Name_Value_Sequence(0..9);
...
X(5) := new Name_Value'( Integer_Type, +"Fred", 23 );
The amount of memory space used up by the allocated value will be the space
required for the actual components selected by the discriminant (in this
case Integer_Value) plus the mandatory components (in this case The_Name)
plus (usually) the discriminants themselves (in this case Value_Type) plus
whatever administrative information is required (associated with the
management of the heap). Usually the administrative information is small
(sometimes it is zero). We should also count the size of the access value
in the array. Usually an access value is also small.
Another example of using indirection:
type Name_Value_Ptr is access all Name_Value;
type Name_Value_Sequence is array (Natural range <>) of Name_Value_Ptr;
Here Name_Value_Ptr is a general access type (note the 'all' in the
declaration). You can create values of the designated type (Name_Value) by
allocating them as before. However, you can also refer to aliased objects
anywhere in memory. For example:
function "+" (Right: String)
return Unbounded_String renames To_Unbounded_String;
X: Name_Value_Sequence(0..9);
...
Y: aliased Name_Value := ( Integer_Type, +"Fred", 23 );
...
X(5) := Y'Access;
The memory used by this technique is the same, except that if we do not
allocate objects in the heap, we avoid the space used up by the
administrative information (however much that is). A general access type
may be bigger than a pool-specific one (but I think they are usually the
same size, in fact).
Finally, an example of using default discriminants:
type Name_Value (Value_Type : Name_Value_Type := String_Type) is
record
...
type Name_Value_Sequence is array (Natural range <>) of Name_Value;
...
X: Name_Value_Sequence(0..9);
...
X(5) := ( Integer_Type, +"Fred", 23 );
The memory space used up by each component of the array will be the space
required for the maximum of the union of the size of every component which
could be selected by the discriminant (in this case the greatest of the
sizes of String_Value, Integer_Value, and The_Value) plus the mandatory
components (in this case The_Name) plus (usually) the discriminants
themselves (in this case Value_Type).
Therefore, if your discriminated record has some variants that are much
bigger than others, this option could be very wasteful of memory space. On
the other hand, if the variants are all of similar size (or memory space is
not a major worry), this option could be the best, since it involves no
indirection, no allocation, and no administrative information.
There are many other possible ways of resolving the problem.
One idea is to have separate arrays, one for each variation. This approach
allows you to make each array of an appropriate length. For example, if you
know that most of your values will be Float_Type, you could make that array
big, and the other two small. The disadvantage of this approach is that you
have to solve the problem of other data referring to array components: now
there are many arrays, the right array has to be selected in addition to an
index into the array. One advantage is that you do not have to store the
discriminants, as such; in some cases this could save a lot of memory.
A possible alternative to using an array is to use a structure with
pointers (usually access values) to form a linked list, a tree, or
whichever structure suits the data and the way it needs to be accessed.
Typically, most of the hard work can be taken out of this approach by
taking advantage of one of the various 'container' types provided by
libraries such as Charles:
http://home.earthlink.net/~matthewjheaney/charles/
Some containers are able to contain indefinite types (such as a record with
discriminants without default values), and can provide various convenient
ways of accessing the data too.
A radical solution is to bypass the typing system. You have an array of
small memory buffers, or you just have one big memory buffer (just like the
heap), and store things in there in an encoding that suits the data. This
approach can be a lot of work (especially to debug), and it may require a
trade-off between memory use and speed of access, but it can be a very
efficient option. In a few cases, it may be the only practical option.
Finally, it should be mentioned that an alternative to using a single
discriminant to simply select one of a set of variants is to use a
hierarchy of tagged types. For example:
type Root_Name_Value_Pair is abstract tagged
record
The_Name : Unbounded_String;
end record;
type Name_String_Pair is new Root_Name_Value_Pair with
record
String_Value : Unbounded_String;
end record;
type Name_Integer_Pair is new Root_Name_Value_Pair with
record
Integer_Value : Integer;
end record;
type Name_Float_Pair is new Root_Name_Value_Pair with
record
The_Value : Float;
end record;
type Name_Value_Access is access Root_Name_Value_Pair'Class;
type Name_Value_Sequence is
array (Positive range <>) of Name_Value_Access;
With tagged types, it is usually necessary to use indirection (access
types) to cope with the fact that polymorphism almost inevitably involves
indefinite types.
The alleged advantages of this approach vary from the subtle and arguable
("it is clearer") to the sure but possibly more complex: each different
type within the hierarchy can have its own 'overriding' body of a
subprogram; the hierarchy can be extended (in separate library units)
without disturbing the existing source text. A call can dynamically select
the correct body of an overridden subprogram, based on the tag of a
parameter; this is a very neat technique, but it isn't always necessarily
the better choice.
Note that in the above example I've used Positive for the 'confinement'
subtype of the array. It is my habit to do this for arrays used in this
typical 'sequence' role, just like Ada's standard String type. People used
to C arrays might prefer Natural. It can be significant if positional array
aggregates are used, since the lower bound will determined by the
confinement subtype (1 for Positive or 0 for Natural).
Some food for thought, hopefully!
--
Nick Roberts
next prev parent reply other threads:[~2003-11-18 17:48 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-11-17 16:57 How to define an array of a variant record type? Harald Schmidt
2003-11-17 17:35 ` Stephen Leake
2003-11-20 10:02 ` Craig Carey
2003-11-17 17:37 ` Marius Amado Alves
2003-11-18 2:48 ` Steve
2003-11-18 9:04 ` Marius Amado Alves
2003-11-17 18:45 ` Rodrigo Garcia
2003-11-18 2:48 ` Steve
2003-11-18 17:48 ` Nick Roberts [this message]
2003-11-19 2:38 ` Steve
2003-11-19 8:11 ` Preben Randhol
2003-11-19 13:26 ` Rodrigo Garcia
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox