Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Variant Record Component

2 views
Skip to first unread message

John Harbaugh

unread,
Feb 26, 2003, 1:00:54 PM2/26/03
to
Hi, all -

Is there a way to determine if an object of some variant record type
contains a specific variant component, other than a trying it and
handling a possible constraint error? I looked for something like and
"in" operator or suitable attribute, but found nothing.

Thanks in advance,

- John Harbaugh

Stephen Leake

unread,
Feb 26, 2003, 2:08:41 PM2/26/03
to
John Harbaugh <john.s.h...@boeing.com> writes:

> Hi, all -
>
> Is there a way to determine if an object of some variant record type
> contains a specific variant component, other than a trying it and
> handling a possible constraint error? I looked for something like and
> "in" operator or suitable attribute, but found nothing.

The discriminant of a variant record tells you what components are
present, but that information is essentially hard-coded everywhere.

It sounds like you want to do introspection or something. ASIS is
good for that, but not at run time.

Perhaps if you give a higher level view of what you are trying to do,
I could be more helpful.

--
-- Stephe

David C. Hoos

unread,
Feb 26, 2003, 3:50:00 PM2/26/03
to comp.lang.ada mail to news gateway

----- Original Message -----
From: "John Harbaugh" <john.s.h...@boeing.com>
Newsgroups: comp.lang.ada
To: <comp.l...@ada.eu.org>
Sent: Wednesday, February 26, 2003 12:00 PM
Subject: Variant Record Component


> Hi, all -
>
> Is there a way to determine if an object of some variant record type
> contains a specific variant component, other than a trying it and
> handling a possible constraint error? I looked for something like and
> "in" operator or suitable attribute, but found nothing.

Well, this sort of thing should not be necessary if the accesses to the
record components are done distinctly for different values of the
discriminant as part of a case statement;


>
> Thanks in advance,
>
> - John Harbaugh

> _______________________________________________
> comp.lang.ada mailing list
> comp.l...@ada.eu.org
> http://ada.eu.org/mailman/listinfo/comp.lang.ada
>

tmo...@acm.org

unread,
Feb 26, 2003, 4:37:47 PM2/26/03
to
> Is there a way to determine if an object of some variant record type
> contains a specific variant component, other than a trying it and
> handling a possible constraint error?
Do you mean a situation with a non-simple "case" like:
type r(v : integer) is record
case v is
when 0 => a:integer;
when 1 | 2 | 3 => b:integer;
when others => null;
end case;
end record;
where you would essentially have to replicate the "case" statement,
and you would rather just ask "if x.b exists"?

Anders Wirzenius

unread,
Feb 27, 2003, 3:17:20 AM2/27/03
to
"Stephen Leake" <Stephen...@nasa.gov> wrote in message news:uof4y5...@nasa.gov...

Sorry, John, for using your thread as a stepping-stone. ;-(

Here, Stephe, is another higher level description:

I HAVE:
1.
Six phone numbers: 1795, 2006, 2007, 2012, 2013, 2014.
2.
A log (text file) from the company's phone system with data like: From_Phone_Nr, To_Phone_Nr, Answering_Time, Duration...

I WANT TO:
3.
Pick only those log data where the To_Phone_Nr is one of the six numbers.
4.
Set up some statistics about those phone calls.
5.
Be able to add or remove phone numbers from the list (six becomes seven some sunny winterday).

I WISH I HAD:
type Help_Desk is (1795, 2006, 2007, 2012, 2013, 2014); -- with the dynamics described under point 5.
If To_Phone_Nr in Help_Desk then
-- Do statistics
end if;

What is a proper way to implement "To_Phone_Nr in Help_Desk" ?


Anders


John McCabe

unread,
Feb 27, 2003, 3:46:40 AM2/27/03
to
On Thu, 27 Feb 2003 08:17:20 GMT, "Anders Wirzenius"
<anders.w...@pp.qnet.fi> wrote:


>Here, Stephe, is another higher level description:
>
>I HAVE:
>1.
>Six phone numbers: 1795, 2006, 2007, 2012, 2013, 2014.
>2.
>A log (text file) from the company's phone system with data like: From_Phone_Nr, To_Phone_Nr, Answering_Time, Duration...
>
>I WANT TO:
>3.
>Pick only those log data where the To_Phone_Nr is one of the six numbers.
>4.
>Set up some statistics about those phone calls.
>5.
>Be able to add or remove phone numbers from the list (six becomes seven some sunny winterday).
>
>I WISH I HAD:
>type Help_Desk is (1795, 2006, 2007, 2012, 2013, 2014); -- with the dynamics described under point 5.
>If To_Phone_Nr in Help_Desk then
> -- Do statistics
>end if;
>
>What is a proper way to implement "To_Phone_Nr in Help_Desk" ?

Use Pascal sets :-)

Best Regards
John McCabe

To reply by email replace 'nospam' with 'assen'

Stephen Leake

unread,
Feb 27, 2003, 12:26:50 PM2/27/03
to
"Anders Wirzenius" <anders.w...@pp.qnet.fi> writes:

> Sorry, John, for using your thread as a stepping-stone. ;-(

Please learn how to change the subject in your newsreader; hijacking
threads like this is simply not acceptable.

> I HAVE:
> 1.
> Six phone numbers: 1795, 2006, 2007, 2012, 2013, 2014.
> 2.
> A log (text file) from the company's phone system with data like:
> From_Phone_Nr, To_Phone_Nr, Answering_Time, Duration...
>
> I WANT TO:
> 3.
> Pick only those log data where the To_Phone_Nr is one of the six numbers.
> 4.
> Set up some statistics about those phone calls.
> 5.
> Be able to add or remove phone numbers from the list (six becomes
> seven some sunny winterday).
>
> I WISH I HAD:
> type Help_Desk is (1795, 2006, 2007, 2012, 2013, 2014); -- with the
> dynamics described under point 5.

Well, since Ada enumeration types don't have the dynamics from point
5, you can't use them.

> If To_Phone_Nr in Help_Desk then
> -- Do statistics
> end if;
>
> What is a proper way to implement "To_Phone_Nr in Help_Desk" ?

Use a SAL binary tree
(http://users.erols.com/leakstan/Stephe/Ada/Sal_Packages/index.htm) to
store the help desk phone numbers; then do

if Is_Present (Help_Tree, To_Phone_Nr) then
...
end if;

Hmm, my web page is out of date; that version doesn't have Is_Present.
I'll update it one of these days...

--
-- Stephe

tmo...@acm.org

unread,
Feb 27, 2003, 1:09:33 PM2/27/03
to
>> Six phone numbers: 1795, 2006, 2007, 2012, 2013, 2014.
> Use a SAL binary tree

If all the OP wants is a Presence test on a small number of possibilities,
a simple loop seems appropriate. If high speed is required (seems
unlikely for this application...), a Boolean lookup array
type Phone_Numbers is range 1000 .. 9999;
Is_Present : constant array(Phone_Numbers) of Boolean
:= (1795 | 2006 | 2007 | 2012 | 2013 | 2014 => True,
others=>False);
would do the job.

Matthew Heaney

unread,
Feb 27, 2003, 7:07:16 PM2/27/03
to
Stephen Leake <Stephen...@nasa.gov> wrote in message news:<uk7fliqh...@nasa.gov>...

> "Anders Wirzenius" <anders.w...@pp.qnet.fi> writes:
>
> > I HAVE:
> > 1.
> > Six phone numbers: 1795, 2006, 2007, 2012, 2013, 2014.
> > 2.
> > A log (text file) from the company's phone system with data like:
> > From_Phone_Nr, To_Phone_Nr, Answering_Time, Duration...
> >
> > I WANT TO:
> > 3.
> > Pick only those log data where the To_Phone_Nr is one of the six numbers.
> > 4.
> > Set up some statistics about those phone calls.
> > 5.
> > Be able to add or remove phone numbers from the list (six becomes
> > seven some sunny winterday).
> >
> > I WISH I HAD:
> > type Help_Desk is (1795, 2006, 2007, 2012, 2013, 2014); -- with the
> > dynamics described under point 5.
>
> > What is a proper way to implement "To_Phone_Nr in Help_Desk" ?
>
> Use a SAL binary tree
> (http://users.erols.com/leakstan/Stephe/Ada/Sal_Packages/index.htm) to
> store the help desk phone numbers; then do
[ex snipped]

If you just want a membership test, you can always use a set:

package Phone_Number_Type is
new Charles.Sets.Sorted.Unbounded (Phone_Number_Type);

Set : Phone_Number_Types.Container_Type;

if Find (Set, Phone_Number) /= Back (Set) then ...

If you need to store some data with each number, you can could still
use a set:

type Info_Type is
record
Phone_Number : Phone_Number_Type;
...
end record;

function "<" (L, R : Info_Type) return Boolean is
begin
return L.Phone_Number < R.Phone_Number;
end;

package Phone_Number_Info is
new Charles.Sets.Sorted.Unbounded (Info_Type, "<");

Set : Phone_Number_Info.Container_Type;

procedure Op (X : Phone_Number_Type) is
I : constant Iterator_Type := Find (Set, X);
begin
if I /= Back (Set) then
declare
Info : Info_Type renames To_Access (I).all;
begin
--manipulate Info as necessary
end;
end if;
end Op;

You could also use a map, which stores the phone number separately
from the info:

package Phone_Number_Info is
new Charles.Maps.Sorted.Unbounded
(Phone_Number_Type,
Info_Type); --extra info only, sans phone number

Map : Phone_Number_Info.Container_Type;

procedure Op (X : Phone_Number_Type) is
I : constant Iterator_Type := Find (Map, X);
begin
if I /= Back (Map) then
declare
Info : Info_Type renames To_Access (I).all;
begin
--manipulate Info as necessary
end;
end if;
end Op;

To add another phone number, just insert it into the set or map. For
example:

procedure Add
(X : Phone_Number_Type;
Y : Info_Type) is

I : Iterator_Type;
Success : Boolean;
begin
Insert (Map, X, Y, I, Success);

if not Success then
--X already in map
end if;
end Add;

You could also use the hashed versions, and use the phone number
itself as the value of the hash function for the phone number key.
Then your lookups would only have O(1) average time complexity.

The hashed associative containers are nice because you can preallocate
the actual hash table:

Map : Hashed_Map_Subtype;

Resize (Map, Size => 6);

That guarantees there's no hash table allocation until the load factor
exceeds 1.

http://home.earthlink.net/~matthewjheaney/charles/

Drop me a line if you have any questions about Charles.

Matt

Anders Wirzenius

unread,
Feb 28, 2003, 1:46:19 AM2/28/03
to

"Stephen Leake" <Stephen...@nasa.gov> wrote in message news:uk7fliqh...@nasa.gov...

> "Anders Wirzenius" <anders.w...@pp.qnet.fi> writes:
>
> > Sorry, John, for using your thread as a stepping-stone. ;-(
>
> Please learn how to change the subject in your newsreader; hijacking
> threads like this is simply not acceptable.
>

I actaully was about to change the subject, but cancelled it because the solution to my problem might be to use records.

Once again, John, sorry for using your thread, I hope I have not directed the community away from helping you with your original
subject.

Anders

John Harbaugh

unread,
Feb 28, 2003, 11:15:52 AM2/28/03
to
Here's the situation: I'm using a variant record to represent a Link 16
(JTIDS) J7.6 message for memory-mapped I/O. So a message comes in and,
based on the message header, a particular structure is frozen. Now I
pass the message object to a decode function that is looking for
particular message fields. Well, surprise surprise, we occasionally
raise Constraint_Error because the field in question is not present.

Several options come to mind (in order of preferance):
* Refactor the function and pass only the variant part instead of the
entire message

* Pass the discriminant value in to the function and have the function
check it before referencing the variant part

* Add a nested declare block in the function to try the variant part and
handle the exception locally

My question has more to do with understanding what language-level tools
that are available. Ada is pretty slick at testing membership of a
value within/without a subtype range. I was wondering if there was
something analogous for variant records. I'm getting the impression
that the answer is no. Yes?

Thanks for your replies,

- John

tmo...@acm.org

unread,
Feb 28, 2003, 1:18:53 PM2/28/03
to
> based on the message header, a particular structure is frozen. Now I
> pass the message object to a decode function that is looking for
> particular message fields. Well, surprise surprise, we occasionally
> raise Constraint_Error because the field in question is not present.
Could you show what you mean? What is "frozen"? Are you getting a
Constraint_Error because of errors in the data (a field is out of range,
for instance), or because the program referenced msg.b when the
discriminant/case statement said there was no field "b"? In the latter
case, it sounds like the symptom is the attempt to reference msg.b, but the
problem is erroneously thinking this message is of the subtype with a "b".

David C. Hoos

unread,
Feb 28, 2003, 2:40:36 PM2/28/03
to comp.lang.ada mail to news gateway
If I understand you correctly, you have defined a variant
records for the variants of the J7.6 message.

It seems to me, then, that the discriminant would be based
upon the Label, Sublabel, and the Message length fields of
the message. Correct?

If this is the case, then I would think that a case statement
covering all of the possible values of the discriminant would
prevent the code from looking for fields that are not present
in that variant.

In my own fairly significant experience with the generation
and interpretation of tactical messages has not been to
use variant records. For bit-oriented messages I use a bit
array overlaid on the in-memory message, and use a
specification of the message format to pick out the pieces
of the message.

The original motivation for this approach is that my task
was to provide interoperability between various systems
that express the same or similar information with differing
message formats and communication protocols. One of the
things I found in doing this was that despite various systems
having been built (supposedly) to the same specifications,
the implementations differ.

As a consequence of this we used a text-based message
format specification (e.g., XML) that could be changed in
the field in order to interpret or produce messages that
were produced by or accepted by the systems actually
encountered.

Thus, the different approach.


Randy Brukardt

unread,
Feb 28, 2003, 3:51:43 PM2/28/03
to
John Harbaugh wrote in message <3E5F8B38...@boeing.com>...

>Here's the situation: I'm using a variant record to represent a Link
16
>(JTIDS) J7.6 message for memory-mapped I/O. So a message comes in and,
>based on the message header, a particular structure is frozen. Now I
>pass the message object to a decode function that is looking for
>particular message fields. Well, surprise surprise, we occasionally
>raise Constraint_Error because the field in question is not present.
>
>Several options come to mind (in order of preferance):
>* Refactor the function and pass only the variant part instead of the
>entire message
>
>* Pass the discriminant value in to the function and have the function
>check it before referencing the variant part

I'm confused. How can you pass the message object to a function without
passing the discriminant? You can't pass part of a record in Ada, and if
you're passing the whole record, you're also passing the discriminant.
After all, if the generated code for the function is checking the value
of the discriminant, it must be available inside the function.

So, just test the discriminant inside the function. You're correct that
there is no convinient syntax for doing so (which is why tagged
extension records are a better choice than a variant for many uses), but
certainly it can be written. If you can arrange the variants to use
subtypes for their selectors, you can use memberships to make this more
maintainable. This would look something like:

type Code_Type is range 0 .. 100;
subtype Error_Codes is Code_Type range 90..100;
subtype Result_Codes is Code_Type range 1 .. 10;
subtype OK_Code is Code_Type range 0 .. 0;
...

type Message_Type (Code : Code_Type) is record
Header : ....;
case Code is
when OK_Code => null;
when Result_Codes => Result : Integer;
when Error_Codes => Error_Message : String(1..40);
...
end case;
end record;

function Text_Message_Decode (Message : in Message_Type) return
String is
begin
case Message.Code is
when OK_Code =>
return "OK";
when Error_Codes =>
return Message.Error_Message;
when Result_Codes =>
return "Result=" & Integer'Image(Message.Result);
...
end case;
end Text_Message_Decode;

By using subtypes in this way, you won't have to change any of the uses
when the codes are changed (unless you add a variant). And if you add a
variant, the Ada completeness checking will insure that it's added to
all of the cases. Just avoid the temptation to use an others clause. We
use this extensively in the intermediate code for Janus/Ada, and the
checking has prevented a great many bugs.

Randy.


John Harbaugh

unread,
Feb 28, 2003, 5:07:20 PM2/28/03
to
Freezing rules are defined in ARM 13.14. In short, freezing is the
point either in compilation or elaboration at which the memory structure
of an entity must be fixed. Because of these rules, the following
representation clause is illegal:

type Byte is mod 2**8;

subtype Nibble is Byte range 0..2*4;
for Nibble'Size use 4;

In the case of the variant record in question, the record type does not
have a defaulted discriminant (a definite type). When a record object
is declared, a discriminant value must be supplied (a constrained
object), at which point the structure of the object is "frozen" at
compile time. If, on the other hand, the record type had a default
expression for the discriminant (an indefinite type) and the object
declaration did not override the default (an unconstrained object), then
freezing would also occur at runtime, because the structure could change
(through whole-value assignment). Variant records a are a little subtle
in this regard.

You are correct, the field "b" may not exist, in which case a
Constraint_Error is raised when the nonexistent field is referenced.

So, I guess it really is unnecessary to have a language feature to test
the structure. One can simply test the discriminant. In this
particular case, I would still advocate for re-factoring the routine
because it is obscenely big, but that's another issue.

Cheers,

- John

Jeffrey Carter

unread,
Feb 28, 2003, 9:34:28 PM2/28/03
to
John Harbaugh wrote:
>
> My question has more to do with understanding what language-level tools
> that are available. Ada is pretty slick at testing membership of a
> value within/without a subtype range. I was wondering if there was
> something analogous for variant records. I'm getting the impression
> that the answer is no. Yes?

No. Subtype membership is defined for all types. For a variant record
type, you can say

type Disc is (Alpha, Bravo, Charlie);

type Variant (D : Disc) is record
case D is
when Alpha =>
...


...
end case;
end record;

subtype Variant_Alpha is Variant (D => Alpha);

V : Variant := Some_Function;

...

if V in Variant_Alpha then

This will work if you've done a good design, and each Disc value has its
own variant part. If most of the variant parts have multiple
discriminant values then this is less useful.

I once saw a project (Ada 83, pre 1995) that had problems because they'd
done something like

type Disc is range 1 .. 100;

type Variant (D : Disc) is record
case D is
when 1 .. 10 =>
...
when 11 .. 20 =>
...


...
end case;
end record;

What they really had was a set of about 10 main classes, each of which
had a specific ID for one of about 10 specific things. What they should
have done was something like

type Disc is (One, Two, ..., Ten);
type ID_One is (One_One, One_Two, ..., One_Ten);
type ID_Two is (Two_One, Two_Two, ..., Two_Ten);
...
type ID_Ten is (Ten_One, Ten_Two, ..., Ten_Ten);

-- The project would have had more meaningful names,
-- but since it was a classified project I can't tell
-- you what they would have been.

type Variant (D : Disc) is record
case D is
when One =>
One_ID : ID_One;
...
when Two =>
Two_ID : ID_Two;
...


...
end case;
end record;

--
Jeff Carter
"C++ is like giving an AK-47 to a monk, shooting him
full of crack and letting him loose in a mall and
expecting him to balance your checking account
'when he has the time.'"
Drew Olbrich

John McCabe

unread,
Mar 3, 2003, 4:24:54 AM3/3/03
to
On Fri, 28 Feb 2003 16:15:52 GMT, John Harbaugh
<john.s.h...@boeing.com> wrote:

>Here's the situation: I'm using a variant record to represent a Link 16
>(JTIDS) J7.6 message for memory-mapped I/O. So a message comes in and,
>based on the message header, a particular structure is frozen. Now I
>pass the message object to a decode function that is looking for
>particular message fields. Well, surprise surprise, we occasionally
>raise Constraint_Error because the field in question is not present.

Areyou sure Constraint_Error is being raised because the field is not
present?

Are you checking the validity bits in the JTIDS message? Obviously if
the validity bits identify a field as being not supplied, trying to
process that field may result in a Constraint_Error if the data is out
of range.

0 new messages