Recent

Author Topic: Proposal: unions as a new type and arrays with number of elements  (Read 20854 times)

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Lately I've been working with records that should have a variable-type content and it drives me crazy when I have to declare such structures. Can Free Pascal finally make readable unions instead of forcing the use of variant-record-abomination? Their syntax is so idiotic that it is hard to believe that someone who has seen Pascal at least once in their life was responsible for their design.

Finally, I suggest that unions be implemented legibly, so that they can be conveniently declared and matched to the structures used in the C language. There must be a normal, Pascal syntax, the ability to create named fields (of any type) and the ability to declare fields under a field that is a union (which is impossible with variant-records, without declaring additional structures).

Suggested syntax:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = union
  3.     // union fields
  4.   end;
  5.  

The same as in the case of records. All fields of such a union occupy the same memory area. For example:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = union
  3.     Letter: Char;  // TFoo.Letter
  4.     Code:   UInt8; // TFoo.Code
  5.   end;
  6.  

The size of such a union is one byte. Unions can have fields of simple types, but also arrays, structures, and other unions. A more complex example to illustrate nesting:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = union
  3.     Chunks: union                           // TFoo.Chunks
  4.       Number: UInt32;                       // TFoo.Chunks.Number
  5.       Words:  array [0 .. 1] of UInt16;     // TFoo.Chunks.Words
  6.       Bytes:  union                         // TFoo.Chunks.Bytes
  7.         Unsigned: array [0 .. 3] of UInt8;  // TFoo.Chunks.Bytes.Unsigned
  8.         Signed:   array [0 .. 3] of Int8;   // TFoo.Chunks.Bytes.Signed
  9.       end;
  10.     end;
  11.     Other: UInt32;                          // TFoo.Other
  12.   end;
  13.  

The size of TFoo union is 8 bytesChunks occupies 4 bytes, Other occupies 4 bytes (is below the union). This syntax is not only readable and consistent with the syntax of other Pascal blocks (explicit termination of the union body, including a nested union), but also allows fields to be declared as unions, and subsequent fields (including structures and unions) below them. You don't need to declare complex field types outside of the main union body. Thus, even very complex unions containing many fields, including nested unions and structures, can be declared as a single and readable data type.

I am asking FPC developers to consider implementing support for such unions to say goodbye to stupid variant-records once and for all. By the way — I don't know why, but the snippet syntax highlighter recognizes the word union and colors it like Pascal's keywords. Good sign. 8)
« Last Edit: May 01, 2023, 10:22:16 pm by furious programming »
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12118
  • FPC developer.
Re: New structure data type — union
« Reply #1 on: April 27, 2023, 05:48:21 pm »
It is extremely unlikely to reimplement working features for stylistic reasons only. 

And even if, I would frown on language proposals with examples that even don't show the intended method of having multiple fields per union case, a feat the "horrible" syntax DOES do.

Similarly, to map onto C you need anonymous unions (which your example also doesn't showcase), as the biggest problem of the current Pascal way is that nesting requires changing the reference syntax.

If some change would be done, I'd bet it be a minimal change proposal that allowed anonymous nesting (union without field  name) to achieve closer mapping with the current syntax, not putting a complete new syntax next to it.

TRon

  • Hero Member
  • *****
  • Posts: 4157
Re: New structure data type — union
« Reply #2 on: April 27, 2023, 05:51:23 pm »
I feel and share your pain furious programmer. Especially the part that you can't (easily) add another field after the variant part makes it very difficult to make concise definitions for supporting both big and small endian structures.

I asked the same questions (some eons ago) and got the response that it can be solved with existing constructs (which is true). That basically means (to my understanding) if it does not add something new other then so called sugar coating then there is not really a incentive to add such a feature.

I can respect that, though this particular issue sometimes feels like using a broomstick to get yourself form point a to b. Unless you have some magic then that is fine, otherwise a bus (or even a bicycle or having good footwear) would be the better option  :D
« Last Edit: April 27, 2023, 05:53:07 pm by TRon »
Today is tomorrow's yesterday.

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Re: New structure data type — union
« Reply #3 on: April 27, 2023, 06:10:06 pm »
It is extremely unlikely to reimplement working features for stylistic reasons only.

As you know, you can't currently declare fields in a variant-record, under a common part, so it is not for stylistic reasons only.

Quote
And even if, I would frown on language proposals with examples that even don't show the intended method of having multiple fields per union case, a feat the "horrible" syntax DOES do.

I didn't give all possible examples, because it's rather redundant and you can guess some things. Yes, declaring anonymous fields as nested unions is necessary and not unusual.

Quote
If some change would be done, I'd bet it be a minimal change proposal that allowed anonymous nesting (union without field  name) to achieve closer mapping with the current syntax, not putting a complete new syntax next to it.

It's not about being able to declare anonymous fields, it's about being able to declare normal unions as single data structures, with the ability to declare fields under common parts, without having to declare completely separate data types outside the union. And for a better syntax and higher readability, because the current one is a joke.
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

korba812

  • Sr. Member
  • ****
  • Posts: 463
Re: New structure data type — union
« Reply #4 on: April 28, 2023, 12:07:27 am »
Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = union
  3.     Chunks: union                           // TFoo.Chunks
  4.       Number: UInt32;                       // TFoo.Chunks.Number
  5.       Words:  array [0 .. 1] of UInt16;     // TFoo.Chunks.Words
  6.       Bytes:  union                         // TFoo.Chunks.Bytes
  7.         Unsigned: array [0 .. 3] of UInt8;  // TFoo.Chunks.Bytes.Unsigned
  8.         Signed:   array [0 .. 3] of Int8;   // TFoo.Chunks.Bytes.Signed
  9.       end;
  10.     end;
  11.     Other: UInt32;                          // TFoo.Other
  12.   end;
  13.  

The size of TFoo union is 8 bytesChunks occupies 4 bytes, Other occupies 4 bytes (is below the union).

Maybe I don't fully understand your proposal, but shouldn't SizeOf(TFoo) be 4? If I understand correctly, the pascal equivalent is:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = record
  3.     case Boolean of
  4.     True: (Chunks: record                              // TFoo.Chunks
  5.       case Byte of
  6.       0: (Number: UInt32);                             // TFoo.Chunks.Number
  7.       1: (Words:  array [0 .. 1] of UInt16);           // TFoo.Chunks.Words
  8.       2: (Bytes:  record                               // TFoo.Chunks.Bytes
  9.         case Boolean of
  10.           True: (Unsigned: array [0 .. 3] of UInt8);   // TFoo.Chunks.Bytes.Unsigned
  11.           False: (Signed:   array [0 .. 3] of Int8);   // TFoo.Chunks.Bytes.Signed
  12.         end;);
  13.       end;);
  14.     False: (Other: UInt32;)                            // TFoo.Other
  15.   end;
  16.  

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Re: New structure data type — union
« Reply #5 on: April 28, 2023, 12:54:47 am »
That's the problem with variant-records, that their functionality is limited and their syntax is tragic.

To answer your question, no, your structure is wrong because Other occupies the same memory space as Chunks and should be after it. The union should be 8 bytes and your structure is 4 bytes. The only solution, given the current syntax and variant-records, is to declare Chunks as a separate type and use it to declare a field. Like this:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFooChunks = record
  3.   case Byte of
  4.     0: (Number: UInt32);                      // TFoo.Chunks.Number
  5.     1: (Words: array [0 .. 1] of UInt16);     // TFoo.Chunks.Words
  6.     2: (Bytes: record                         // TFoo.Chunks.Bytes
  7.     case Byte of
  8.       0: (Unsigned: array [0 .. 3] of UInt8); // TFoo.Chunks.Bytes.Unsigned
  9.       1: (Signed:   array [0 .. 3] of Int8);  // TFoo.Chunks.Bytes.Signed
  10.     end; );
  11.   end;
  12.  
  13. type
  14.   TFoo = record
  15.     Chunks: TFooChunks; // TFoo.Chunks
  16.     Other:  UInt32;     // TFoo.Other
  17.   end;
  18.  

My eyes bleed when I see code like this — I can't even format it properly... That's why Free Pascal should start supporting true unions, and by the way not force you to use this monstrous syntax, with stupid nested case of and whose body is not completed.

The syntax I would like, i.e. the one that allows you to declare unions clearly, consistently and without any restrictions, is in the example below. Pay attention not only to the declaration of the union, but also to the indexation of the arrays (number of elements, not dumb ranges).

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = union
  3.     Chunks: union                     // TFoo.Chunks
  4.       Number: UInt32;                 // TFoo.Chunks.Number
  5.       Words:  array[2] of UInt16;     // TFoo.Chunks.Words
  6.       Bytes:  union                   // TFoo.Chunks.Bytes
  7.         Unsigned: array[4] of UInt8;  // TFoo.Chunks.Bytes.Unsigned
  8.         Signed:   array[4] of Int8;   // TFoo.Chunks.Bytes.Signed
  9.       end;
  10.     end;
  11.     Other: UInt32;                    // TFoo.Other
  12.   end;
  13.  

In this particular example, nested unions are to be named because the expected syntax for accessing these fields is as in the comments. Which does not change the fact that there should be support for unnamed fields for those that are records and unions.
« Last Edit: April 28, 2023, 01:05:02 am by furious programming »
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

korba812

  • Sr. Member
  • ****
  • Posts: 463
Re: New structure data type — union
« Reply #6 on: April 28, 2023, 01:04:40 am »
I was confused that you use "union" for variable records and regular records. So in Pascal it looks like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = record
  3.     Chunks: record                              // TFoo.Chunks
  4.       case Byte of
  5.       0: (Number: UInt32);                             // TFoo.Chunks.Number
  6.       1: (Words:  array [0 .. 1] of UInt16);           // TFoo.Chunks.Words
  7.       2: (Bytes:  record                               // TFoo.Chunks.Bytes
  8.         case Boolean of
  9.           True: (Unsigned: array [0 .. 3] of UInt8);   // TFoo.Chunks.Bytes.Unsigned
  10.           False: (Signed:   array [0 .. 3] of Int8);   // TFoo.Chunks.Bytes.Signed
  11.         end;);
  12.       end;
  13.     Other: UInt32;                            // TFoo.Other
  14.   end;
  15.  

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Re: New structure data type — union
« Reply #7 on: April 28, 2023, 01:06:29 am »
You used the additional record to group the common part, to be able to declare something below it. It works, but syntactically sucks, the readability is poor (in my opinion).
« Last Edit: April 28, 2023, 01:08:06 am by furious programming »
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

korba812

  • Sr. Member
  • ****
  • Posts: 463
Re: New structure data type — union
« Reply #8 on: April 28, 2023, 01:21:46 am »
But exactly the same you show in your example only instead of record you use union. Maybe pascal syntax isn't very pretty, but it's been known for years and isn't confusing. Why another keyword for something that already exists?

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Re: New structure data type — union
« Reply #9 on: April 28, 2023, 01:45:19 am »
If you like code stuffed with garbage, well, stick with it. I don't like that, especially when the syntax forces code obfuscation with worthless data such as case of's, their cases and meaningless parentheses that don't match the syntax of structures, when a simple and readable union end is enough. It is the same simple as record end.
« Last Edit: April 28, 2023, 01:49:40 am by furious programming »
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

440bx

  • Hero Member
  • *****
  • Posts: 5080
Re: New structure data type — union
« Reply #10 on: April 28, 2023, 03:56:37 am »
The "problems" in Pascal variants/unions are two-fold. 

The first problem is that Pascal has no anonymous/unnamed unions.  This makes it cumbersome to translate some C structs/unions.

The second problem is that Pascal forces the variant to be the last field and because of that "overloads" the "end;" to end the record _and_ the variant as well (personally, that is the part I dislike the most, it breaks syntactic symmetry.)

One of the "pluses" in Pascal's syntax is that every variant/union has to be individually identified, usually by a value in a case enumeration, which is something that is not required in C ("union" along with open close brace is all that is needed.)  While this may look like a small thing, if a C struct is incorrectly formatted, it is quite difficult to realize that the formatted view does not represent the real data structure.  This problem is more easily seen visually in Pascal (which is a plus for the Pascal syntax.)

I haven't analyzed all the consequences in the grammar but, it seems that a good place to start would be to require the variant's "case" statement to have its own "end".  That would eliminate the "end" overload and open the door to having unnamed variants as well as eliminating the requirement that they be placed last in the record.

I think the current syntax could be extended (e.g, requiring "end" to terminate the "case") to add the capabilities and flexibility found in the C syntax.  I am not fond of the idea of creating a new syntax to add capabilities that can likely be added by improving the current syntax.
 
« Last Edit: April 28, 2023, 03:59:33 am by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Zvoni

  • Hero Member
  • *****
  • Posts: 2914
Re: New structure data type — union
« Reply #11 on: April 28, 2023, 09:56:30 am »
I'd have to agree with Korba, that your proposal occupies 4 Bytes.
From a Syntax-Point i'd say, that this would occupy 8 Bytes
Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = Record   //HERE!!!
  3.     Chunks: union                           // TFoo.Chunks
  4.       Number: UInt32;                       // TFoo.Chunks.Number
  5.       Words:  array [0 .. 1] of UInt16;     // TFoo.Chunks.Words
  6.       Bytes:  union                         // TFoo.Chunks.Bytes
  7.         Unsigned: array [0 .. 3] of UInt8;  // TFoo.Chunks.Bytes.Unsigned
  8.         Signed:   array [0 .. 3] of Int8;   // TFoo.Chunks.Bytes.Signed
  9.       end;
  10.     end;
  11.     Other: UInt32;                          // TFoo.Other
  12.   end;
TFoo is still a Record (and not a Union).
Chunks and Chunks.Bytes are Unions
« Last Edit: April 28, 2023, 10:02:09 am by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: New structure data type — union
« Reply #12 on: April 28, 2023, 11:22:42 am »
A generic union is not possible (I don't think so anyway).
example

type
union<P,Q>=record
case integer of
0:(A:P);
1:(B:Q);
end;

But a simple generic function sharing a memory slot is useable.
Code: Pascal  [Select][+][-]
  1.  
  2. {$mode objfpc}
  3. {$APPTYPE CONSOLE}
  4. uses
  5. strutils;
  6.  
  7. generic function union<A,B>(x:A):B;
  8. var
  9.  yy:B absolute x;
  10. begin
  11. exit(yy);
  12. end;
  13.  
  14. function rgb(r:byte;g:byte;b:byte) :uint32;
  15.    begin
  16.    exit(((b Shl 16) Or ((g) Shl 8) Or (r) Or $FF000000)- $FF000000)
  17. end;
  18.  
  19. type aob=array[1..3] of byte;
  20. type aoc=array[1..3] of char;
  21.  
  22. var
  23. i:integer;
  24. x:aob;
  25. y:uint32;
  26.  
  27. s:aoc=('1','2','3');
  28. g:aob;
  29.  
  30.  
  31.  
  32. begin
  33. y:=rgb(45,56,78);
  34. write(y,' =  ');
  35. x:=specialize union<uint32,aob>(y);
  36. write ('rgb (');
  37. for i:=1 to 3 do write(x[i],ifthen(i=3,')',','));
  38. writeln;
  39. write('(');
  40. for i:=1 to 3 do write(''''+s[i]+'''',ifthen(i=3,') =  ',','));
  41.  
  42. g:=specialize union<aoc,aob>(s);
  43. write('(');
  44. for i:=1 to 3 do write((g[i]),ifthen(i=3,')',','));
  45. writeln;
  46. writeln('Press enter to exit . . .');
  47. readln;
  48. end.
  49.  
  50.  
« Last Edit: April 28, 2023, 11:31:26 am by BobDog »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12118
  • FPC developer.
Re: New structure data type — union
« Reply #13 on: April 28, 2023, 11:33:39 am »
You used the additional record to group the common part, to be able to declare something below it. It works, but syntactically sucks, the readability is poor (in my opinion).

Those are the stylistic concerns I talked about. There are always people that want to turn good Pascal syntax into C. But in that case, simply use C, not Pascal.

People love to think up syntax, but hate actually fixing existing ones.

Lifting the fields after case of limitation would go a long way.  The transformation of foreign headers then becomes a simple textual transformation. But watch packing rules, those can cause invisible compatibilities.

That exists for reasons in Pascal that are not in the FPC/Delphi dialects. (in Standard Pascal, if you instantiate a pointer to the union, you can specify the variant you want and it will allocate exactly enough)
« Last Edit: April 28, 2023, 11:36:46 am by marcov »

flowCRANE

  • Hero Member
  • *****
  • Posts: 911
Re: New structure data type — union
« Reply #14 on: April 28, 2023, 01:12:32 pm »
I'd have to agree with Korba, that your proposal occupies 4 Bytes.

You are right, so to be clear, the 8-byte-long structure with nested union:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = record
  3.     Chunks: union                     // TFoo.Chunks
  4.       Number: UInt32;                 // TFoo.Chunks.Number
  5.       Words:  array[2] of UInt16;     // TFoo.Chunks.Words
  6.       Bytes:  union                   // TFoo.Chunks.Bytes
  7.         Unsigned: array[4] of UInt8;  // TFoo.Chunks.Bytes.Unsigned
  8.         Signed:   array[4] of Int8;   // TFoo.Chunks.Bytes.Signed
  9.       end;
  10.     end;
  11.     Other: UInt32;                    // TFoo.Other
  12.   end;
  13.  

Thanks for your vigilance.



There are always people that want to turn good Pascal syntax into C.

Good Pascal syntax? This is not good syntax, it is a convoluted way of declaring a union that is unreadable and unconsistent with other Pascal blocks. If I didn't see the problem with unions in Pascal, I wouldn't create this thread and complain.

Quote
But in that case, simply use C, not Pascal.

Tell me how to use C in Pascal code and I'll gladly use C-unions instead of variant-records.

Quote
People love to think up syntax, but hate actually fixing existing ones.

This thread was not created to change the way variant-records are declared, but to consider extending the syntax to include the ability to freely declare unions, in a convenient, Pascal-like way. An extension, not a modification, because backward compatibility must be maintained (which should be obvious to everyone).
Lazarus 3.6 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on a retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

 

TinyPortal © 2005-2018