comp.lang.ada
 help / color / mirror / Atom feed
* How to define an array of a variant record type?
@ 2003-11-17 16:57 Harald Schmidt
  2003-11-17 17:35 ` Stephen Leake
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Harald Schmidt @ 2003-11-17 16:57 UTC (permalink / raw)


Hi,

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?

Harald






^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  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
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Stephen Leake @ 2003-11-17 17:35 UTC (permalink / raw)


Harald Schmidt <office@anobject.net> writes:

>    type Name_Value_Type is (String_Type,
>                             Integer_Type,
>                             Float_Type);
> 
>    type Name_Value(Value_Type : Name_Value_Type) is

change this to

type Name_Value(Value_Type : Name_Value_Type := String_Type) is


This seems very odd, but it works. It says to the compiler "let the
discriminant change at runtime". Without the default value for the
discriminant, the discriminant is _not_ allowed to change at runtime;
it must be known at compile time.

-- 
-- Stephe



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  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-17 17:37 ` Marius Amado Alves
  2003-11-18  2:48   ` Steve
  2003-11-17 18:45 ` Rodrigo Garcia
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Marius Amado Alves @ 2003-11-17 17:37 UTC (permalink / raw)
  To: comp.lang.ada

To simulate arrays of indefinite or unconstrained elements in Ada you
must use pointers (access types). Define an access type to your logical
type, then define an array of such pointers. Your life will be slightly
facilitated as Ada provides implicit dereference in most situations.




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  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-17 17:37 ` Marius Amado Alves
@ 2003-11-17 18:45 ` Rodrigo Garcia
  2003-11-18  2:48 ` Steve
  2003-11-18 17:48 ` Nick Roberts
  4 siblings, 0 replies; 12+ messages in thread
From: Rodrigo Garcia @ 2003-11-17 18:45 UTC (permalink / raw)


"Harald Schmidt" <office@anobject.net> wrote in message
news:BBDEBC6C.902D%office@anobject.net...
> Hi,
>
> 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?
>
> Harald

I would use "Indirection": Use an array of _accesses_ to the variant record.

type Name_Value_Ptr is access Name_Value;
type Name_Value_Sequence is array (Natural range <>) of Name_Value_Ptr;

Rodrigo





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-17 17:37 ` Marius Amado Alves
@ 2003-11-18  2:48   ` Steve
  2003-11-18  9:04     ` Marius Amado Alves
  0 siblings, 1 reply; 12+ messages in thread
From: Steve @ 2003-11-18  2:48 UTC (permalink / raw)


"Marius Amado Alves" <amado.alves@netcabo.pt> wrote in message
news:mailman.17.1069090659.3110.comp.lang.ada@ada-france.org...
> To simulate arrays of indefinite or unconstrained elements in Ada you
> must use pointers (access types). Define an access type to your logical
> type, then define an array of such pointers. Your life will be slightly
> facilitated as Ada provides implicit dereference in most situations.
>

Dynamic allocation is inherently more risky (having potential for memory
leaks) than simply using a discriminated record with a default discriminant.

Steve
(The Duck)





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-17 16:57 How to define an array of a variant record type? Harald Schmidt
                   ` (2 preceding siblings ...)
  2003-11-17 18:45 ` Rodrigo Garcia
@ 2003-11-18  2:48 ` Steve
  2003-11-18 17:48 ` Nick Roberts
  4 siblings, 0 replies; 12+ messages in thread
From: Steve @ 2003-11-18  2:48 UTC (permalink / raw)


To do this you must use a "default discriminant".  In your record type
definition use:

  type Name_Value( Value_Type : Name_Value_Type := String_Type ) is
  ...

There are a couple of other things you should be aware of.
1. When assigning values to the record, you can only change the type by
doing a complete record assignment.

  That is:
     Value : Name_Value;

     Value := ( Value_Type => String_Type,        --this is ok
                     String_Value => To_Unbounded_String( "Hello" );

     Value.Value_Type := Integer_Type;    -- will not work

     Value := ( Value_Type => Integer_Type,    -- ok
                     Integer_Value => 42 );

     Value.Integer_Value := 43; -- ok


2. You can only change the discriminant through the record assignment if the
record was declared using the default.

  That is:
     Value : Name_Value(Integer_Type);

     Value := ( Value_Type => String_Type,        -- will not work.
                     String_Value => To_Unbounded_String( "Hello" );


Steve
(The Duck)


"Harald Schmidt" <office@anobject.net> wrote in message
news:BBDEBC6C.902D%office@anobject.net...
> Hi,
>
> 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?
>
> Harald
>
>
>





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-18  2:48   ` Steve
@ 2003-11-18  9:04     ` Marius Amado Alves
  0 siblings, 0 replies; 12+ messages in thread
From: Marius Amado Alves @ 2003-11-18  9:04 UTC (permalink / raw)
  To: comp.lang.ada

On Tue, 2003-11-18 at 02:48, Steve wrote:
> "Marius Amado Alves" <amado.alves@netcabo.pt> wrote:
> > To simulate arrays of indefinite or unconstrained elements in Ada you
> > must use pointers...
> Dynamic allocation is inherently more risky (having potential for memory
> leaks) than simply using a discriminated record with a default discriminant.

You're right. Default discriminants is a better way for arrays of
variant records. I had forgotten such types are definite (and can
therefore be array components).




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-17 16:57 How to define an array of a variant record type? Harald Schmidt
                   ` (3 preceding siblings ...)
  2003-11-18  2:48 ` Steve
@ 2003-11-18 17:48 ` Nick Roberts
  2003-11-19  2:38   ` Steve
  2003-11-19 13:26   ` Rodrigo Garcia
  4 siblings, 2 replies; 12+ messages in thread
From: Nick Roberts @ 2003-11-18 17:48 UTC (permalink / raw)


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




^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-18 17:48 ` Nick Roberts
@ 2003-11-19  2:38   ` Steve
  2003-11-19  8:11     ` Preben Randhol
  2003-11-19 13:26   ` Rodrigo Garcia
  1 sibling, 1 reply; 12+ messages in thread
From: Steve @ 2003-11-19  2:38 UTC (permalink / raw)


One other thing I can't resist mentioning.

Here is a snippet of Ada Code in oue of our systems.

  TYPE aConstantDataItemCAX (itemTypeCAX : aConstantDataTypeCAX :=
invalidTypeCAX) IS
    RECORD
      CASE itemTypeCAX IS
        WHEN invalidTypeCAX | iTypeCAX =>
          --The data item that is used to pass data between screen edit
          --and the access procedures
          intValueCAX : integer;
        WHEN fTypeCAX =>
          realValueCAX : float;
        WHEN eTypeCAX =>
          eValueCAX : anEnumRecordCAX;
        WHEN sTypeCAX =>
          stringValueCAX : aConstantStringCAX;
      END CASE;
    END RECORD;

Funny how the same ideas come up again and again.  The original code was
written in Pascal around 1987.

Steve
(The Duck)





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-19  2:38   ` Steve
@ 2003-11-19  8:11     ` Preben Randhol
  0 siblings, 0 replies; 12+ messages in thread
From: Preben Randhol @ 2003-11-19  8:11 UTC (permalink / raw)


On 2003-11-19, Steve <nospam_steved94@comcast.net> wrote:
> One other thing I can't resist mentioning.
>
> Here is a snippet of Ada Code in oue of our systems.
>
>   TYPE aConstantDataItemCAX (itemTypeCAX : aConstantDataTypeCAX :=
> invalidTypeCAX) IS
>     RECORD
>       CASE itemTypeCAX IS
>         WHEN invalidTypeCAX | iTypeCAX =>
>           --The data item that is used to pass data between screen edit
>           --and the access procedures
>           intValueCAX : integer;
>         WHEN fTypeCAX =>
>           realValueCAX : float;
>         WHEN eTypeCAX =>
>           eValueCAX : anEnumRecordCAX;
>         WHEN sTypeCAX =>
>           stringValueCAX : aConstantStringCAX;
>       END CASE;
>     END RECORD;

Wouldn't underscores been nice?


-- 
"Saving keystrokes is the job of the text editor, not the programming
 language."



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-18 17:48 ` Nick Roberts
  2003-11-19  2:38   ` Steve
@ 2003-11-19 13:26   ` Rodrigo Garcia
  1 sibling, 0 replies; 12+ messages in thread
From: Rodrigo Garcia @ 2003-11-19 13:26 UTC (permalink / raw)


> So, to summarise the various answers to this question, you could use
> indirection or default discriminants.

There is a third possibility. Use tagged types instead of variant records
and... welcome to the wonderful world of OO programming in Ada  ;^)

Rodrigo





^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: How to define an array of a variant record type?
  2003-11-17 17:35 ` Stephen Leake
@ 2003-11-20 10:02   ` Craig Carey
  0 siblings, 0 replies; 12+ messages in thread
From: Craig Carey @ 2003-11-20 10:02 UTC (permalink / raw)


On 17 Nov 2003 12:35:34 -0500, Stephen Leake wrote:
...
>change this to
>
>type Name_Value(Value_Type : Name_Value_Type := String_Type) is
>
>
>This seems very odd, but it works. It says to the compiler "let the
>discriminant change at runtime". Without the default value for the
>discriminant, the discriminant is _not_ allowed to change at runtime;
>it must be known at compile time.


http://www.adaic.org/standards/95aarm/html/AA-3-7.html

AARM 3.7 Discriminants
...
NOTES
28  50  If a discriminated type has default_expressions for its
   discriminants, then unconstrained variables of the type are
   permitted, and the values of the discriminants can be changed by an
   assignment to such a variable. If defaults are not provided for the
   discriminants, then all variables of the type are constrained,
   either by explicit constraint or by their initial value; the values
   of the discriminants of such a variable cannot be changed after
   initialization. 

28.a  Discussion: This connection between discriminant defaults and
   unconstrained variables can be a source of confusion. For Ada 95,
   we considered various ways to break the connection between defaults
   and unconstrainedness, but ultimately gave up for lack of a
   sufficiently simple and intuitive alternative.
...


Presumably pesons could use Ada for 10 years without figuring that out,
except for slow Leake of information at comp.lang.ada.

----

Also there is the pragma Volatile trick (that won't run in GNAT 3.15p
due to the caching not going away): the discriminant is altered by
using X'Address or similar, and pragma Volatile suppresses the caching.






^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2003-11-20 10:02 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2003-11-19  2:38   ` Steve
2003-11-19  8:11     ` Preben Randhol
2003-11-19 13:26   ` Rodrigo Garcia

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