Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-10

Case in Record

26 messages · 12 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

章宏九

In the document
(http://www.freepascal.org/docs-html/ref/refsu15.html#x38-450003.3.2) I read the code below:

Type
  MyRec = Record
          X : Longint;
          Case byte of
            2 : (Y : Longint;
                 case byte of
                 3 : (Z : Longint);
                 );
          end;

I still cannot understand what do these two "byte" mean. I have difficulty in understanding "The optional identifier in the case statement serves to access the tag field value, which otherwise would be invisible to the programmer" and need some explain. Thank you.

Another problem is about Object and Class. I notice that in wiki (http://wiki.freepascal.org/Object_Oriented_Programming_with_FreePascal_and_Lazarus), Class is explained as a pointer to the structure, while Object returns the whole structure. And the wiki said that Object is not used very much. Does this mean that Object is deprecated? Can Objects contain any properties?

Reply by email

Henry Vermaak
to 章宏九

2009/10/6 章宏九 :

Show quoted text 18 lines
In the document (http://www.freepascal.org/docs-html/ref/refsu15.html#x38-450003.3.2) I read the code below: Type  MyRec = Record          X : Longint;          Case byte of            2 : (Y : Longint;                 case byte of                 3 : (Z : Longint);                 );          end; I still cannot understand what do these two "byte" mean. I have difficulty in understanding "The optional identifier in the case statement serves to access the tag field value, which otherwise would be invisible to the programmer" and need some explain. Thank you.

Look at the second example on this page:

http://delphibasics.co.uk/RTL.asp?Name=Record

Show quoted text 7 lines
Another problem is about Object and Class. I notice that in wiki (http://wiki.freepascal.org/Object_Oriented_Programming_with_FreePascal_and_Lazarus), Class is explained as a pointer to the structure, while Object returns the whole structure. And the wiki said that Object is not used very much. Does this mean that Object is deprecated? Can Objects contain any properties?

Look at this page for an explanation of the basic difference:

http://freepascal.org/docs-html/ref/refch6.html

I can't really comment on the scary details, since I'm rubbish at object oriented programming :)

Henry

Reply by email

Felipe Monteiro de Carvalho
to 章宏九: In the document (http://www.freepascal.org/docs-html/ref/refsu15.html#x38-450003.3.2) I…
章宏九 wrote 9 lines
Type  MyRec = Record          X : Longint;          Case byte of            2 : (Y : Longint;                 case byte of                 3 : (Z : Longint);                 );          end;

I think the cases are useless here. AFAIK the case inside record is utilized to give multiple options of use for the same memory area. It is used so that the same structure can have multiple uses without wasting space. But this is pointless if the case has only 1 option, it should have 2 or more to make sense. For me the above is the same as:

Type
  MyRec = Record
     X : Longint;
     Y: Longint;
     Z: Longint;
  end;

I think that visibility isn't in place because even with multiple options you can access any of them at any given time even if they conflict.

And the wiki said that Object is not used very much. Does this mean that Object is deprecated?

Deprecated means that the feature will or may be removed in the future, but AFAIK there are no plans to remove support for Object. So I would say it's a legacy feature (from Turbo Pascal), but not deprecated at the moment.

If you are not sure which one to use, then just use classes.

Felipe Monteiro de Carvalho

Reply by email

Frank Peelo
to Felipe Monteiro de Carvalho: I think the cases are useless here. AFAIK the case inside record is utilized to give…
Felipe Monteiro de Carvalho wrote 15 lines
On Tue, Oct 6, 2009 at 10:13 AM, 章宏九 wrote: >Type > MyRec = Record > X : Longint; > Case byte of > 2 : (Y : Longint; > case byte of > 3 : (Z : Longint); > ); > end; I think the cases are useless here. AFAIK the case inside record is utilized to give multiple options of use for the same memory area.

Yup. Like you say, this is a wierd example.

It's called a "variant record". Google for that term. http://www.delphicorner.f9.co.uk/articles/op6.htm
That means, a record structure where one or more fields occupy the same memory, but have different types. Like a union in C. So depending on which field you read, the memory area can be read in different types.

So if you have
Type
   MyRec = Record
     Case byte of
     1: (X : Integer;);
     2: (Y : Word;);
   end;
var
   v : MyRec;
Begin
   v.y := 65535;
   writeln(v.x);
end.

assigning 65535 to v.y sets the underlying bytes to $FF, $FF so v.X is -1.

Now, one would not normally do this (writing to one field and reading the other). If you write to one field, the memory under the other one(s) is messed up. So you should only read from the field that you write to.

How do you know which field to read from?

Normally, you would have a tag field. This is a field with a specific ordinal type coming before the variant fields.

Type
   MyRec = Record
     Case FieldType:byte of
     1: (X : Integer;);
     2: (Y : Word;);
   end;

Now, if you are writing to field X, you should write a 1 to FieldType. Elsewhere in your program, you can read FieldType, and if it is 1 you read x. If it is 2 you read y. And if it is some other value you output an error message...

I think the syntax with just a type and no tag field name (case byte of) was invented afterwards, to handle the case when you don't want a tag field.

...

Show quoted text 8 lines
>And the wiki said that Object is not used very >much. Does this mean that Object is deprecated? Deprecated means that the feature will or may be removed in the future, but AFAIK there are no plans to remove support for Object. So I would say it's a legacy feature (from Turbo Pascal), but not deprecated at the moment.

Object was introduced in the first object-oriented Borland Pascal, TurboPascal 5.5. To use polymorphism, you access objects through a pointer (because if you say var o:tMyObject; then you *know* what type it is). When Delphi came along, seems Borland decided that everyone should *always* use pointers for object types, and introduced the Class, which is always on the heap and handled as a pointer. Any new concepts that were introduced, like properties, were applied only to the new classes and objects were left in a corner, like the embarrassing relative that you wish you could get rid of, but can't. IMHO objects are the less confusing of the two, but they seem to be, not quite deprecated, but discouraged. So as Felipe said:

If you are not sure which one to use, then just use classes.

FP

Reply by email

章宏九
to Henry Vermaak: 2009/10/6 Henry Vermaak : And objects aren't deprecated, since the compiler uses them…

Thank you. I saw all the examples you gave, but I still cannot master how to use a variant record without a specified tag.

For example:
type
  TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
  TFigure = record
    case TShapeList of
      Rectangle: (Height, Width: Real);
      Triangle: (Side1, Side2, Angle: Real);
      Circle: (Radius: Real);
      Ellipse, Other: ();
  end;
var
  Figure1: TFigure;

How can I specified the TShapelist value in a Figure1?

2009/10/6 Henry Vermaak :

Show quoted text 12 lines
2009/10/6 Henry Vermaak : > > I can't really comment on the scary details, since I'm rubbish at > object oriented programming :) And objects aren't deprecated, since the compiler uses them (afaik). Henry _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Henry Vermaak
to 章宏九

2009/10/6 章宏九 :

Show quoted text 17 lines
Thank you. I saw all the examples you gave, but I still cannot master how to use a variant record without a specified tag. For example: type  TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);  TFigure = record    case TShapeList of      Rectangle: (Height, Width: Real);      Triangle: (Side1, Side2, Angle: Real);      Circle: (Radius: Real);      Ellipse, Other: ();  end; var  Figure1: TFigure; How can I specified the TShapelist value in a Figure1?

You can specify an identifier in the case statement, which will become a new field. You can then specify what the record is used for currently, e.g.:

TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
TFigure = record
  case Shape: TShapeList of
          ^^^^^^^
    Rectangle: (Height, Width: Real);
    Triangle: (Side1, Side2, Angle: Real);
    Circle: (Radius: Real);
    Ellipse, Other: ();
  end;

Then you can set .Shape to Rectangle, for instance, and fill in .Height and .Width. A function receiving a variable of this record can then see what shape it is currently used for. You need to maintain this .Shape field, though.

Henry

Reply by email

Jürgen Hestermann
to 章宏九: Thank you. I saw all the examples you gave, but I still cannot master how to use a…
Thank you. I saw all the examples you gave, but I still cannot master how to use a variant record without a specified tag.

If you use a specific tag as in

type TFigure = record
                case ShapeName : TShapeList of
                   Rectangle: (Height, Width: Real);
                   Triangle: (Side1, Side2, Angle: Real);
                   Circle: (Radius: Real);
                   Ellipse, Other: ();
                   end;

then you will get an additional part in your record named "ShapeName" which can (but not necessarily must) be used to store the meaning of the record variants. If you don't use such a variable name as in

type TFigure = record
                case TShapeList of
                   Rectangle: (Height, Width: Real);
                   Triangle: (Side1, Side2, Angle: Real);
                   Circle: (Radius: Real);
                   Ellipse, Other: ();
                   end;

then you have to detect the meaning of the variants in some other way. In both cases you can directly access the variants as in TFigure.Triangle or TFigure.Rechtangle. But if you stored with one variant and read with another you will get a lot of mess. ;-)

Reply by email

Anton Tichawa
to 章宏九: Thank you. I saw all the examples you gave, but I still cannot master how to use a…
章宏九 wrote 16 lines
Thank you. I saw all the examples you gave, but I still cannot master how to use a variant record without a specified tag. For example: type TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other); TFigure = record case TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; var Figure1: TFigure;

AFAIK there has to be some fixed part before the variant, e. g.

TFigure = record
    name: string;
    case TShapeList of
      Rectangle: (Height, Width: Real);
      Triangle: (Side1, Side2, Angle: Real);
      Circle: (Radius: Real);
      Ellipse, Other: ();
  end;

The program could then determine from the contents of the "name" field which variant is used in a certain instance. This saves memory ("TShapeList" has no memory reserved at run time), but the program is responsible to correctly identify the variant using the "name" field.

Anton

Reply by email

章宏九
to Anton Tichawa

I see. That means if no tag name is given, I should make sure my code is correct by myself.

Thank you.

2009/10/7 Anton Tichawa :

Show quoted text 41 lines
On Tue, 2009-10-06 at 23:37 +0800, 章宏九 wrote: > Thank you. I saw all the examples you gave, but I still cannot master > how to use a variant record without a specified tag. > > For example: > type >   TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other); >   TFigure = record >     case TShapeList of >       Rectangle: (Height, Width: Real); >       Triangle: (Side1, Side2, Angle: Real); >       Circle: (Radius: Real); >       Ellipse, Other: (); >   end; > var >   Figure1: TFigure; > AFAIK there has to be some fixed part before the variant, e. g. TFigure = record    name: string;    case TShapeList of      Rectangle: (Height, Width: Real);      Triangle: (Side1, Side2, Angle: Real);      Circle: (Radius: Real);      Ellipse, Other: ();  end; The program could then determine from the contents of the "name" field which variant is used in a certain instance. This saves memory ("TShapeList" has no memory reserved at run time), but the program is responsible to correctly identify the variant using the "name" field. Anton _______________________________________________ fpc-pascal maillist  -   http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Jürgen Hestermann
to Anton Tichawa: AFAIK there has to be some fixed part before the variant, e. g. TFigure = record name:…
Show quoted text 11 lines
AFAIK there has to be some fixed part before the variant, e. g. TFigure = record name: string; case TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; The program could then determine from the contents of the "name" field which variant is used in a certain instance.

How would that work? "name" is just a string which you can assign an arbitrary value to. The compiler cannot store additional information in it.

The detection of the correct variant is completely up to the programmer. There is no help by the compiler.

Therefore, there is absolutly no difference between

TFigure = record
           X : TShapeList;
           case TShapeList of
              Rectangle: (Height, Width: Real);
              Triangle: (Side1, Side2, Angle: Real);
              Circle: (Radius: Real);
              Ellipse, Other: ();
              end;

and

TFigure = record
           case X : TShapeList of
              Rectangle: (Height, Width: Real);
              Triangle: (Side1, Side2, Angle: Real);
              Circle: (Radius: Real);
              Ellipse, Other: ();
              end;

It ends up in the same record and also the programmer does not see any difference. He can store whatever he wants in X in both cases. It has no influences on the variants.

Reply by email

Anton Tichawa
to Jürgen Hestermann
Jürgen Hestermann wrote 24 lines
Therefore, there is absolutly no difference between TFigure = record X : TShapeList; case TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; and TFigure = record case X : TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; It ends up in the same record and also the programmer does not see any difference. He can store whatever he wants in X in both cases. It has no influences on the variants.

True. The second example has better style, though.

But the point is that X can be completely omitted.

E. g. you have the freedom to encode the information contained in X in some other way. It might be derived from a name or a part number.

Anton

Reply by email

Nikolay Nikolov
to Frank Peelo: Yup. Like you say, this is a wierd example. It's called a "variant record". Google for…
Frank Peelo wrote
Any new concepts that were introduced, like properties, were applied only to the new classes and objects were left in a corner, like the embarrassing relative that you wish you could get rid of, but can't.

However, AFAIK Free Pascal supports properties in objects as well as global properties (that is properties, that act like a global variable, outside any object or class). But that's a Free Pascal specific extension, it's not supported by Delphi. Which is a non-issue for me, since I only use FPC these days :)

Reply by email

Vincent Snijders
to Jürgen Hestermann: How would that work? "name" is just a string which you can assign an arbitrary value to.…
Jürgen Hestermann wrote 24 lines
Therefore, there is absolutly no difference between TFigure = record X : TShapeList; case TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; and TFigure = record case X : TShapeList of Rectangle: (Height, Width: Real); Triangle: (Side1, Side2, Angle: Real); Circle: (Radius: Real); Ellipse, Other: (); end; It ends up in the same record and also the programmer does not see any difference. He can store whatever he wants in X in both cases. It has no influences on the variants.

IIRC, there is a difference. With the second declaration the compiler can add a run-time check that the correct memebrs are accessed based on the value of X.

Unfortunately, I cannot find the compiler switch.

Vincent

Reply by email

Jonas Maebe
to Vincent Snijders
Vincent Snijders wrote 5 lines
IIRC, there is a difference. With the second declaration the compiler can add a run-time check that the correct memebrs are accessed based on the value of X. Unfortunately, I cannot find the compiler switch.

FPC has no support for this.

Jonas

Reply by email

Vincent Snijders
to Jonas Maebe
Jonas Maebe wrote 11 lines
On 07 Oct 2009, at 09:19, Vincent Snijders wrote: > IIRC, there is a difference. With the second declaration the compiler > can add a run-time check that the correct memebrs are accessed based > on the value of X. > > Unfortunately, I cannot find the compiler switch. FPC has no support for this.

Then I did not remember correctly: I thought you implemented it. :-)

Vincent

Reply by email

Florian Klaempfl
to Vincent Snijders
Show quoted text 14 lines
Jonas Maebe schreef: > > On 07 Oct 2009, at 09:19, Vincent Snijders wrote: > >> IIRC, there is a difference. With the second declaration the compiler >> can add a run-time check that the correct memebrs are accessed based >> on the value of X. >> >> Unfortunately, I cannot find the compiler switch. > > FPC has no support for this. > Then I did not remember correctly: I thought you implemented it. :-)

Actually I though too that Jonas implemented this once :)

Reply by email

Jonas Maebe
to Florian Klaempfl
Florian Klaempfl wrote 18 lines
Vincent Snijders schrieb: > Jonas Maebe schreef: >> >> On 07 Oct 2009, at 09:19, Vincent Snijders wrote: >> >>> IIRC, there is a difference. With the second declaration the >>> compiler >>> can add a run-time check that the correct memebrs are accessed >>> based >>> on the value of X. >>> >>> Unfortunately, I cannot find the compiler switch. >> >> FPC has no support for this. > > Then I did not remember correctly: I thought you implemented it. :-) Actually I though too that Jonas implemented this once :)

I once started working on it, but never finished it (and the variantcase.patch file in my compiler dir is extremely small, so I never got very far either).

Jonas

Reply by email

Jürgen Hestermann
to Jonas Maebe: FPC has no support for this. Jonas
Show quoted text 5 lines
> IIRC, there is a difference. With the second declaration the compiler > can add a run-time check that the correct memebrs are accessed based > on the value of X. > Unfortunately, I cannot find the compiler switch. FPC has no support for this.

And I don't know any other Pascal Compiler who does any checks in this direction. Do some?

Reply by email

Florian Klaempfl
to Jonas Maebe
Show quoted text 8 lines
On 07 Oct 2009, at 17:39, Jürgen Hestermann wrote: > And I don't know any other Pascal Compiler who does any checks in this > direction. Do some? I don't know, but ADA reportedly does. Such a feature definitely seems useful to me.

Afaik GPC does as well?

Reply by email

Frank Peelo
to Jürgen Hestermann: And I don't know any other Pascal Compiler who does any checks in this direction. Do some?
Jürgen Hestermann wrote 10 lines
>> IIRC, there is a difference. With the second declaration the compiler >> can add a run-time check that the correct memebrs are accessed based >> on the value of X. >> Unfortunately, I cannot find the compiler switch. > > FPC has no support for this. And I don't know any other Pascal Compiler who does any checks in this direction. Do some?

Given that the OP was asking about learning, maybe it would be worth mentioning that the way we normally have "data structures which hold different data" these days is by polymorphism in objects or classes.

Instead of having an array of variant records, each of which might hold a description of a circle, square or pentagon depending on the value of a tag variable, we would now have an array of Shape, where Shape is a class instance or pointer to an object. The actual elements of the array would be Circle, Square or Pentagon, where these are types descended from Shape.

This has several benefits, not least the fact that new shapes can be added more easily than in the variant record case.

The stuff that has been said about variant records is all good, and variant records still have uses in some cases, but we don't want to mislead the OP into thinking that this is necessarily the way to go with new programs.

FP

Reply by email

Jürgen Hestermann
to Frank Peelo
Show quoted text 9 lines
Given that the OP was asking about learning, maybe it would be worth mentioning that the way we normally have "data structures which hold different data" these days is by polymorphism in objects or classes. Instead of having an array of variant records, each of which might hold a description of a circle, square or pentagon depending on the value of a tag variable, we would now have an array of Shape, where Shape is a class instance or pointer to an object. The actual elements of the array would be Circle, Square or Pentagon, where these are types descended from Shape.

But if you look at it closely OP boils down to the same fundamental "pointers pointing to memory" logic too. You only don't tell the user. And that's something I hate with OP: You don't know what happens in detail (at least, it mostly isn't told you in any help about a programming language). I have a clear imagination about pointers pointing to memory addresses and variant records. But how are arrays of different objects (with different sizes) implemented? I suspect that it's simply an array of pointers pointing to the objects (spread all over the heap). But noone tells the users so they don't know exactly what happens under the hood and therefore may choose bad designs.

This has several benefits, not least the fact that new shapes can be added more easily than in the variant record case.

If you code this by yourself (array of pointers pointing to variant records) then it's the same as your OP design but at least you know how it is coded and where it can be optimized.

Reply by email

Jürgen Hestermann
to Jonas Maebe: I don't know, but ADA reportedly does. Such a feature definitely seems useful to me. Jonas
> And I don't know any other Pascal Compiler who does any checks in this > direction. Do some? I don't know, but ADA reportedly does.

I don't know the differences to Pascal. Does it have the same syntax for variant records?

Such a feature definitely seems useful to me.

Of course it would be. But how should it work? Setting the tag variable when assigning a value and raising an error when reading with different variant? Then what if the tag variable is changed directly? Or should it be readonly (which would make sense IMHO).

Reply by email

Vinzent Höfler
to Jürgen Hestermann

"Jürgen Hestermann" :

>> And I don't know any other Pascal Compiler who does any checks in >> this direction. Do some? > I don't know, but ADA reportedly does.

Yes, but the semantics of discriminants is slightly different than in PASCAL.

I don't know the differences to Pascal. Does it have the same syntax for variant records?

No.

Of course it would be. But how should it work? Setting the tag variable when assigning a value and raising an error when reading with different variant? Then what if the tag variable is changed directly? Or should it be readonly (which would make sense IMHO).

Well, variant records with read-only discriminants don't make much sense IME. They are just too inflexible.

In Ada both variants (discriminant is constant or changeable) are supported. To change the discriminant of such a (unconstrained) variable a whole-record assignment is needed, constrained variables (discriminant is initialized at declaration time) can't change their discriminant anymore. So validity checks can be done on both read and (partial) write accesses.

With the current state of FPC, discriminant-checked records with changeable discriminants would be possible without changing rules or syntax. If you want them read-only the rules must probably change. ;)

Vinzent.

GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT! Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Reply by email

Jerry
to Jürgen Hestermann: I don't know the differences to Pascal. Does it have the same syntax for variant records?…
Jürgen Hestermann wrote 8 lines
>> And I don't know any other Pascal Compiler who does any checks in >> this direction. Do some? > I don't know, but ADA reportedly does. I don't know the differences to Pascal. Does it have the same syntax for variant records?

FWIW, here is a link to the BNF of the Ada Programming Language: http://www.seas.gwu.edu/~adagroup/ada95-syntax/
Jerry

Show quoted text 7 lines
> Such a feature definitely seems useful to me. Of course it would be. But how should it work? Setting the tag variable when assigning a value and raising an error when reading with different variant? Then what if the tag variable is changed directly? Or should it be readonly (which would make sense IMHO).

Reply by email