Author Topic: enumerated type WAS: array values as reference  (Read 1481 times)

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 307
enumerated type WAS: array values as reference
« on: August 15, 2026, 01:39:43 pm »
Not sure what these are called, but I like them. However, I have a need to find out how many elements are in such an array:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}
  7.   cthreads,
  8.   {$ENDIF}
  9.   Classes
  10.   { you can add units after this };
  11.  
  12. type
  13.  //Icons in the TImageList - they should appear in this order:
  14.  TImageIcons = (iiUnidentified,      //Unidentified
  15.                 iiADFSFloppySML,     //ADFS Floppy S/M/L
  16.                 iiADFSFloppyD,       //ADFS Floppy D
  17.                 iiADFSFloppyEF,      //ADFS Floppy E,E+,F,F+
  18.                 iiDFSFloppy,         //DFS Floppy
  19.                 iiWEDFSFloppy,       //Watford Floppy
  20.                 iiC64Floppy,         //Commodore 64 Floppy
  21.                 iiAmigaFloppy,       //Commodore Amiga Floppy
  22.                 iiDOSFloppy,         //DOS Floppy
  23.                 iiSinclairFloppy,    //Sinclair/Amstrad Floppy
  24.                 iiADFSHDDOO,         //ADFS Hard Drive Old Map
  25.                 iiADFSHDDNO,         //ADFS Hard Drive New Map Old Dirs
  26.                 iiADFSHDDNN,         //ADFS Hard Drive New Map New/Big Dirs
  27.                 iiAmigaHDD,          //Commodore Amiga Hard Drive
  28.                 iiDOSHDD,            //DOS Hard Drive
  29.                 iiCassette,          //Cassette (UEF)
  30.                 iiISO,               //ISO (CD)
  31.                 iiROMFS,             //ROMFS
  32.                 iiSparkFS,           //SparkFS
  33.                 iiAcornFS);          //Acorn File Server
  34.  
  35. begin
  36.  WriteLn(TImageIcons(5));          //Shows what the sixth element is
  37.  WriteLn(Ord(iiDOSFloppy));        //Enumerates the reference to a number
  38.  WriteLn(High(TImageIcons));       //Gets the last element
  39.  WriteLn(Ord(High(TImageIcons))+1);//Total number of elements in the list
  40.  WriteLn(SizeOf(TImageIcons));     //Shows how many bytes used to store the array
  41. end.
  42.  

I thought SizeOf did this, but that just returns how much space is used to store the array. The best I can find is a roundabout way, by getting the final element, getting it's ordinal value, then adding one. There must be a simpler way.

Oh, and what are these type of arrays called please?
« Last Edit: August 20, 2026, 04:24:52 pm by geraldholdsworth »

LemonParty

  • Hero Member
  • *****
  • Posts: 651
Re: array values as reference
« Reply #1 on: August 15, 2026, 01:46:58 pm »
It is an enumerated type.
Number of elements:
Code: Pascal  [Select][+][-]
  1. const
  2.   Elements = ord(High(TImageIcons)) + 1;
« Last Edit: August 15, 2026, 01:50:31 pm by LemonParty »
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

paweld

  • Hero Member
  • *****
  • Posts: 1733
Re: array values as reference
« Reply #2 on: August 15, 2026, 02:15:27 pm »
That's not entirely true - with the type definition below, it will return nonsense:
Code: Pascal  [Select][+][-]
  1.      
  2. type
  3.   TImageIcons = (iiUnidentified,    
  4.     iiADFSFloppySML,    
  5.     iiAcornFS = 1000);      
  6.  
  7. begin
  8.   WriteLn(Ord(High(TImageIcons))+1);  //return 1001, instead of 3!!!
  9. end.    
Best regards / Pozdrawiam
paweld

geraldholdsworth

  • Sr. Member
  • ****
  • Posts: 307
Re: array values as reference
« Reply #3 on: August 15, 2026, 02:19:35 pm »
It works for what I need. However, would this work?
Code: Pascal  [Select][+][-]
  1. count:=0;
  2. for i:=Low(TImageIcons) to High(TImageIcons) do inc(count);
  3. WriteLn(count,' elements');

EDIT: Missed the Ord() out.
« Last Edit: August 15, 2026, 02:21:26 pm by geraldholdsworth »

jamie

  • Hero Member
  • *****
  • Posts: 7937
Re: array values as reference
« Reply #4 on: August 15, 2026, 03:37:41 pm »
You can also use this, I guess?  This option does not work if you have defined values in the Enum but does work in your case.

Code: Pascal  [Select][+][-]
  1.  Caption := TypInfo.GetEnumNameCount(TypeInfo(TImageIcons)).Tostring;  
  2.  

Include the TypInfo unit.

Jamie

The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 7937
Re: array values as reference
« Reply #5 on: August 16, 2026, 07:58:57 pm »
I wonder why the TypeInfo has issues with ENUMs that have custom values in it?

The GetEnumNameCount should work either way, it's just counting the indices of the Enum definition?

I believe this also fails in Delphi, too!

Jamie

The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: array values as reference
« Reply #6 on: August 17, 2026, 10:10:10 am »
That is indeed the case for both Delphi and Freepascal.
I once suggested a patch for the case where enum members have a value assignment but it was refused as won't fix some (~8-10?) years ago. You can do it manually, though: if there are "gaps" in the enum, they have no name filled in. I will look up what I did to fix that, but I need to connect an old drive first.

EDIT
Seems there is no typeinfo for enums with assigned values at all. Same as in Delphi
Investigating....
EDIT2 helped by claude for exact formulation (AFTER I wrote the code!!)

  EnumCount<T> — counts declared literals of a (possibly non-sequential)
  enumerated type T.

  Requires FPC >= trunk r49064 / 3.3.1 (fix for Mantis #38642: generics +
  non-sequential enums). Fails to compile on stable 3.2.2 with
  "No type info available for this type".

  T is not restricted to ordinal types at compile time, since FPC has no
  "T: Ordinal" generic constraint yet.
  Non-ordinal T is instead rejected at run time via GetTypeKind(T), which
  returns 0.

  For sparse enums, Write()/IOResult (RTL error 107, documented in the
  User's Guide, Appendix D) distinguishes declared literals from gaps in
  the ordinal range; this does not depend on published RTTI, which sparse
  enums lack.

VERY dirty solution:
Code: Pascal  [Select][+][-]
  1. {$if fpc_fullversion < 30301}{$error 'needs a trunk after #38642 (fixed, revision 49064)'}{$endif}
  2. {$mode delphi}
  3. type
  4.   TWeekdays = (monday = 4,tuesday=100, wednesday,thursday,friday);
  5.  
  6.   function EnumCount<T>:integer;  
  7.   var
  8.     test:T;
  9.     s:string;// dummy
  10.   begin
  11.   {$push}{$I-}
  12.     result := 0; // if T is not an enum, simply return 0.
  13.     if GetTypeKind(T) = tkEnumeration then
  14.     for test :=Low(T) to High(T) do
  15.     begin
  16.       writestr(s,test);// misuse error recovery....This IS needed.
  17.       if IOResult = 0 then inc(result); // count only valid values, not the gaps.
  18.     end;
  19.   {$pop}
  20.   end;  
  21.  
  22. begin
  23.   writeln(EnumCount<TWeekdays>);
  24. end.
 
I still have to find my MR/FR.
The above code works for any enum.
My original proposal was probably a feature request.
« Last Edit: August 18, 2026, 11:32:33 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: array values as reference
« Reply #7 on: August 17, 2026, 09:10:05 pm »
Actually it is not that bad:
It correctly tests if an enum member has a name and if it has not IOResult = 107.
The user manual says:
Code: Text  [Select][+][-]
  1. 107 Invalid enumeration
  2. Reported when a text representation of an enumerated constant cannot be created in a call to str or write(ln).
And that is, in fact, exactly what we want.
The lightweight error handling is also better than using slow exceptions here.
So, for the lack of rtti, this is an acceptable solution and also works in GUI apps.
It is not Delphi compatible, though: Delphi does not accept standalone generic functions.

It needs  main/trunk 3.3.1 after #38642 (fixed, revision 49064)
Updated https://forum.lazarus.freepascal.org/index.php/topic,74787.msg588857.html#msg588857
for a better explanation of the technical details.
« Last Edit: August 18, 2026, 07:54:34 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1607
    • Lebeau Software
Re: array values as reference
« Reply #8 on: August 19, 2026, 08:51:22 pm »
It is not Delphi compatible, though: Delphi does not accept standalone generic functions.

True, but it does allow class/static functions inside a Generic class. So, you could do something like:

Code: Pascal  [Select][+][-]
  1. class function TEnumHelper<T>.GetCount: integer; static;
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: array values as reference
« Reply #9 on: August 20, 2026, 07:30:30 am »
@Remy Lebeau

Yes, I have done something similar and for lack of writestr in Delphi, I tried to solve that by writing to 'NUL' device on Windows and writing to '/dev/null' on unix's.
This does not work, though, for another reason:
Code: Pascal  [Select][+][-]
  1.     if GetTypeKind(T) <> tkEnumeration then exit(0);
  2.     for test :=Low(T) to High(T) do
  3. // this fails in Delphi but is accepted in FPC since r49064
As of yet, I did not find a way to count a sparse enum in Delphi.
Of course regular enums can be counted the same way with Low/High.
( count := High(enum)+1 )

This was my DELPHI attempt (Delphi code, not Freepascal):
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. type
  3.   TWeekdays = (monday = 4,tuesday=100, wednesday,thursday,friday=200);
  4.  
  5.   TEnumHelper<T> = class
  6.     class function GetCount: integer; static;
  7.   end;
  8.  
  9.   class function TEnumHelper<T>.GetCount: integer;
  10.   var
  11.     test:T;
  12.     nul:text;
  13.     dev:string;
  14.   begin
  15.   {$pushopt}{$I-}
  16.     if GetTypeKind(T) <> tkEnumeration then exit(0);
  17.     dev := {$ifdef mswindows}'NUL'{$else}'/dev/null'{$endif};
  18.     system.assign(nul,dev);
  19.     rewrite(nul);
  20.     for test :=Low(T) to High(T) do // <---  Fails in Delphi.....
  21.     begin
  22.       write(nul,test);
  23.       if IOResult = 0 then inc(result); // count only valid values, not the gaps.
  24.     end;
  25.     Close(nul);
  26.   {$popopt}
  27.   end;
  28.  
  29. begin
  30.   writeln(TEnumHelper<TWeekdays>.count);
  31. end.
Although https://stackoverflow.com/questions/1600575/iterate-through-items-in-an-enumeration-in-delphi gives me some ideas.

I have send a private message on how to fix that in Delphi. I am not in the beta testers group anymore, but you are. The code is not relevant to this forum, so private.
« Last Edit: August 20, 2026, 09:57:35 am by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Zvoni

  • Hero Member
  • *****
  • Posts: 3519
Re: array values as reference
« Reply #10 on: August 20, 2026, 11:52:28 am »
As a very, very basic workaround irrespective of FPC-Version

Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode ObjFPC}{$H+}
  3. Uses Sysutils;
  4. Type
  5.   TMyEnum=(meOne,meTwo,meTen,meFifteen,meHundred);
  6. Var
  7.   MyValues:Array[TMyEnum] Of Integer=(1,2,10,15,100);
  8.   i:Integer;
  9. begin
  10.   Writeln(Length(MyValues));
  11.   For i:=Ord(Low(TMyEnum)) To ord(High(TMyEnum)) Do
  12.     Writeln(MyValues[TMyEnum(i)]);
  13.   Readln;
  14. end.
  15.  
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

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: array values as reference
« Reply #11 on: August 20, 2026, 01:26:39 pm »
@Zvoni
No, we are talking about sparse enums (assigned values with lots of non-named values in between) . Of course that does NOT work:
Code: Pascal  [Select][+][-]
  1. type myenum = (read = 10, green = 100 ,blue = 1000);
You did not read...
In Freepascal this is possible to count with my code, but not in Delphi.
Delphi needs a fix in system, not its compiler. (which I have and posted to Remy in private because this is not a Delphi forum)

Bit sloppy by you....Your code and reasoning is usually sound.
You should avoid the array trick. That's the core.

In non-sparse enums like yours the count is simply High(enum)+1. (Duh! everybody knows that!)

The alternative is that there is no generic solution for counting sparse enums in Delphi and you would need to implement it for every enum you want to count because the underlying array needs to be specific.

The output of my code = 3, the other output reads 1001.
Maybe now you understand the problem of solving this?

I am happy to accept any improvements, though.... ;D
« Last Edit: August 20, 2026, 02:05:41 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

paweld

  • Hero Member
  • *****
  • Posts: 1733
Re: array values as reference
« Reply #12 on: August 20, 2026, 01:58:40 pm »
@Thaddy:
... this is not a Delphi forum)
...
Best regards / Pozdrawiam
paweld

Thaddy

  • Hero Member
  • *****
  • Posts: 19805
  • Glad to be alive.
Re: array values as reference
« Reply #13 on: August 20, 2026, 02:07:05 pm »
 :) Indeed. @Paweld but try to improve my code. Dead giveaway: all A.I. did not think of my code....
(And that works just in Freepascal, but with Remy's tip could easily work in Delphi too)
« Last Edit: August 20, 2026, 02:11:28 pm by Thaddy »
Any "programmer" that knows only one programming language is not a programmer

Zvoni

  • Hero Member
  • *****
  • Posts: 3519
Re: array values as reference
« Reply #14 on: August 20, 2026, 03:58:07 pm »
@Zvoni
No, we are talking about sparse enums (assigned values with lots of non-named values in between) . Of course that does NOT work:
Code: Pascal  [Select][+][-]
  1. type myenum = (read = 10, green = 100 ,blue = 1000);
You did not read...
In Freepascal this is possible to count with my code, but not in Delphi.
Delphi needs a fix in system, not its compiler. (which I have and posted to Remy in private because this is not a Delphi forum)

Bit sloppy by you....Your code and reasoning is usually sound.
You should avoid the array trick. That's the core.

In non-sparse enums like yours the count is simply High(enum)+1. (Duh! everybody knows that!)

The alternative is that there is no generic solution for counting sparse enums in Delphi and you would need to implement it for every enum you want to count because the underlying array needs to be specific.

The output of my code = 3, the other output reads 1001.
Maybe now you understand the problem of solving this?

I am happy to accept any improvements, though.... ;D

Thaddy, i did get what this is about. No worries.
It's why i wrote "a very very basic workaround" (!!) --> WORKAROUND
Of course i agree, that it's a dirty trick, but i was actually refering to the Subject/Thread-Title! --> array VALUES as Reference

No discussion about sparse enums with gaps in between "values" should be done "natively" resp. in a generic way, like you demonstrated.
I fully agree with you.
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

 

TinyPortal © 2005-2018