tags:

views:

570

answers:

2

I want to iterate through the items in an enumeration.

I'd like to be able to say something like this:

type
  TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);

...
elementCount := GetElementCount(TypeInfo(TWeekDays));

for i := 0 to elementCount - 1 do begin
  ShowMessage(GetEnumName(TypeInfo(TWeekdays),i));
end;

The closest I've been able to come is this:

function MaxEnum(EnumInfo: PTypeInfo): integer;
const
  c_MaxInt = 9999999;
var
  i: integer;
  s: string;
begin
  //get # of enum elements by looping thru the names
  //until we get to the end.
  for i := 0 to c_MaxInt do begin
    s := Trim(GetEnumName(EnumInfo,i));
    if 0 = Length(s) then begin
      Result := i-1;
      Break;
    end;
  end;
end;

Which I use like this:

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  i, nMax: integer;
begin
  ListBox1.Clear;
  nMax := MaxEnum(TypeInfo(TWeekdays));
  for i := 0 to nMax do begin
    ListBox1.Items.Add(GetEnumName(TypeInfo(TWeekdays),i));
  end;
end;

That works well, except the list I get looks like this (notice the last two items):

wdMonday
wdTuesday
wdWednesday
wdThursday
wdFriday
Unit1
'@'#0'ôÑE'#0#0#0#0#0#0#0#0#0#0#0#0#0  <more garbage characters>

The two items at the end are obviously not what I want.

Is there a better way to iterate through the elements of an enumerated type?

If not, then is it safe to assume that there will always be exactly two extra items using my current method? Obviously one is the Unit name... but what is the "@" symbol doing? Is it really garbage, or is it more type information?

I'm using Delphi 2007. Thanks for any insights.

+16  A: 

Simple:

type
  TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);

procedure Test;
var
  el: TWeekdays;
begin
  for el := Low(TWeekdays) to High(TWeekdays) do
    ; //
end;
gabr
Ok, I am an idiot. I tried this earlier but I left my "el" as an integer, and didn't realize my mistake. Thank you.
JosephStyons
I am still wondering what the "@<garbage>" means, though.
JosephStyons
MaxEnum probably is buggy; returning more than the actual count of enums. And GetEnumName uses that buggy result to analyze the internal structures in your code. What you get is random bits of memory past the internal structures.
utku_karatas
Exactly. What you really want is the High() compiler magic function, as seen in this reply.
Mason Wheeler
GetEnumName trusts you're passing it a valid value. It reads through the TTypeData.NameList field first, and after that is the EnumUnitName field. After that is garbage: A one-byte value that the function interprets as a string length, plus that many characters that the function believes is the string contents. You're lucky you only got two extra strings. To determine the maximum enum value from PTypeInfo, read the MaxValue field: `GetTypeData(TypeInfo(TWeekdays)).MaxValue`. The field is valid for all ordinal types.
Rob Kennedy
A: 

I made an EnumerationEnumerator so you could use it in the 'for ... in' statement in Delphi. However, back then it would sometimes generate internal compiler errors.

Edit:

Managed to get it to work in Delphi 2007 and up, see this blog article (and the quite interesting discussion under it).

Jeroen Pluimers
Interesting! I've never understood why that isn't possible in the first place.
Giel
@Giel: here it is http://wiert.wordpress.com/2009/10/27/delphi-for-in-on-enumerated-data-types/
Jeroen Pluimers