comp.lang.ada
 help / color / mirror / Atom feed
From: "Mark Lundquist" <mark@rational.com>
Subject: Re: Allocate an array of a record with a discriminant?
Date: Fri, 30 Mar 2001 18:48:52 GMT
Date: 2001-03-30T18:48:52+00:00	[thread overview]
Message-ID: <oy4x6.647107$U46.19927727@news1.sttls1.wa.home.com> (raw)
In-Reply-To: tc1ghv4o7s23e2@corp.supernews.co.uk

Hey Joe,

Joe <sorry@no.email> wrote in message
news:tc1ghv4o7s23e2@corp.supernews.co.uk...
> Dear all,
>
> I'd appreciate it greatly if any of you could answer the following
question.
>
> Basically, I want to create an array of English words. The following
> information will be given before the words are typed in, _but_ not before
> runtime:
>
> - the maximum length of any one word (the Max_Length variable will contain
> it)
> - the number of words to be given (Num_Words)

OK, I'm with ya...

>
> A word would be described by something like the following type:
>
> type Word (Max_Length : Integer) is
> record
>  Length : Integer;
>  S : String (1 .. Max_Length);
> end record;

Fair enough...

>
> As I understand it, using discriminants with arrays is not possible such
as:
>
> type Words (Max_Word_Length : Integer) is array (Natural range <>) of Word
> (Max_Word_Length);

You're right, an array type can't be a discriminated type (remember, a
discriminant is a special kind of component, and while that fits into the
idea of a record -- which has named components of arbitrary types -- it
doesn't fit into the idea of an array, where all the components are of the
same type and all are indexable over a range).

>
> How is it possible to allocate space for an array of type "Word" where all
> are of the same "Word.Length" which is "Max_Length" at runtime?
>
> Keep in mind that Max_Length and Num_Words are given at runtime, not
before.

Easy.  I'll show you two ways to do it...

(Disclaimer: I haven't run these examples through a compiler, so some
tweakage may be required :-)

The first way looks like this:

    function Get_Num_Words return Natural is
    begin
        -- Prompt victim for number of words and take input;
        -- I dummied it up to return a hardcoded value
        --
        return 10;
    end Get_Num_Words;

    function Get_Max_Word_Length return Natural is
    begin
        -- Prompt victim for number of words and take input;
        -- I dummied it up to return a hardcoded value
        --
        return 32;
    end Get_Max_Word_Length;

    --
    -- A "word".
    --

    Max_Word_Length : constant Natural := Get_Max_Word_Length;

    type Word is
        record
            Length : Natural;
            S : String (1 .. Max_Word_Length);
        end record;

    --
    -- An array of "words".
    --

    Num_Words : constant Natural := Get_Num_Words;

    type Words is array (1 .. Num_Words) of Word;


OK, notice the following:

1) Here we have the program "doing stuff" (getting user input) *before* all
the type definitions are elaborated.  You can do that!

2) No discriminant is required for type Word.  That should make sense
intuitively, since as you said, all objects of the type have the same "max
length".  Discriminants are for when *not* all values of the type have the
same "whatever".

3) For type Words, we could also have declared it unconstrained:

            type Words is array (Natural range <>) of Word;

more like in your example, and constrained it at the declaration of the
object of that type.  It doesn't make any difference.

Now, let's make a slight change... you can take the Length field of type
Word, and pull it out as a discriminant!  Here's how that looks:

    subtype Word_Length is Natural range 1 .. Max_Word_Length;

    type Word (Length : Word_Length := Max_Word_Length) is
        record
            S  : String (1 .. Length);
        end record;

Get it?  We're using the discriminant for the *length* of each individual
word, not the "max length" like in your example.

Things to note:

1) The discriminant has a default value.

2) We used our own constrained subtype for the subtype of the discriminant

These two things make it possible to declare objects (and components) of
type Word, without including a discriminant constraint in the object
declaration!  This is called an "unconstrained object", and it it's only
allowed if the discriminant has a default value (to satisfy the language
rule that a discriminant must always be initialized).  Unconstrained objects
have the unique property that the discriminant can be changed as the result
of a "whole-value assignment" to the object -- more on that in a bit.  But
first note that because no discriminant constraint is required, you can
declare your array exactly as in the first example I gave:

    type Words is array (1 .. Num_Words) of Word;

Now each element of Words is an unconstrained object.  How much storage is
reserved for each element?  Enough to hold the largest possible value (just
like you wanted!).  How do we know what that is?  It's determined by the
subtype of the discriminant.  That's why we had to define our own subtype
for this; if we had written

    type Word (Length : Natural := Max_Word_Length) is record
        ...

Then the largest possible value would have a string of length Natural'Last,
so you would most likely get an exception (like Storage_Error) at run-time
when elaborating the declaration of an object of the type.

Notice that in this application, the value for the discriminant default does
not matter!  Since we're just going to change the discriminant values of all
these objects anyway, we could have just as well said

    type Word (Length : Word_Length := 0) is record
        ...

It's the subtype that determines how much storage is allocated, not the
discriminant default.

Now, how do you change the discriminant of an unconstrained object?  With a
"whole-value assignment", like this:

    Words (1) := (Length => 6, "foobar");

You would probably make things more convenient by doing something like this:

    function To_Word (S : String) return Word is
    begin
        return Word'(S'Length, S);
    end To_Word;

    Words (1) := To_Word ("foobar");    -- let the compiler worry about the
length

One nice thing about making the word length a discriminant is that each
Word's string component now is sized just right for that word, so you don't
have to slice it to get the right substring.  So instead of something like

    Put_Line (Words(I).S(1 .. Words(I).Length));

you can just say

    Put_Line (Words(I).S);


Hope this helps... As some others pointed out, there are other (maybe
better) ways to skin this cat, like using Ada.Strings.Bounded or
Ada.Strings.Unbounded.  But I thought you might find it helpful to know how
to skin it in the particular way that you asked about :-)

Best,
Mark Lundquist
Rational Software






  parent reply	other threads:[~2001-03-30 18:48 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-03-27 15:41 Allocate an array of a record with a discriminant? Joe
2001-03-27 20:22 ` Robert A Duff
2001-03-27 21:11 ` tmoran
2001-03-30 18:48 ` Mark Lundquist [this message]
2001-03-31  4:22   ` Robert A Duff
  -- strict thread matches above, loose matches on Subject: below --
2001-03-27 19:44 Beard, Frank
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox