Jump to content
Updated RadStudio RoadMap Read more... ×
Lars Fosdal

Delphi pitfalls: Enumerated types and for loops

Recommended Posts

Not all for loops are created equal.

Consider
for x in [value1, value2, value3]

You would expect to see x vary in the order of the values in the list.  However – if x and the values are of an enumerated type, looping the “list” does NOT loop in the apparent order of the constant, but in the order of the enumerated type declaration, such as it would for any set.

 

Example at: https://larsfosdal.blog/2019/02/18/delphi-pitfalls-enumerated-types-and-for-loops/

Share this post


Link to post
2 hours ago, Lars Fosdal said:

You would expect to see x vary in the order of the values in the list.

No I would not because its not a list but a set which are implemented as bitmask and thus have a fixed order.

  • Like 4
  • Thanks 1

Share this post


Link to post
28 minutes ago, Stefan Glienke said:

No I would not because its not a list but a set which are implemented as bitmask and thus have a fixed order.

It is pretty quick to miss that "detail".  They look the same, so unless you really pay attention to the type element type - there is little to tell you that you are looking at a set and not a list.

Share this post


Link to post
2 minutes ago, Stefan Glienke said:

Strictly speaking assuming a specific order in a for-in loop is wrong to begin with.

For a list or any other object enumeration interface, I'd agree - but for an open array or a TArray? 

Share this post


Link to post
Just now, Lars Fosdal said:

For a list or any other object enumeration interface, I'd agree - but for an open array or a TArray? 

I could answer that by quoting your previous comment 😉

 

 

Share this post


Link to post
7 minutes ago, Stefan Glienke said:

I could answer that by quoting your previous comment 😉

 

 

Except it is rare to see a TList or any other object structure "hardcoded" in plain sight, while arrays of simple types are not hard to read nor uncommon.
 

Share this post


Link to post

AFAIK you cannot inline declare an array in a for in loop so having some elements in between square brackets will always be a set.

You can see it in your very own code on your blog article where you had to put them into an array variable first to then process them in the loop.

 

The point I agree with though is that the fact that sets and arrays share its syntax might be confusing sometimes.

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
13 minutes ago, Lars Fosdal said:

For a list or any other object enumeration interface, I'd agree - but for an open array or a TArray? 

A for-in-loop doesn't guarantee some order. Any order seen just relies on the implementation and may be subject to change in the future. (Latest when we compile for quantum computers)

 

I remember someone asking for a backwards for-in-loop, but that request is based on a false assumption in the first place.

Share this post


Link to post

I wonder how it behaves if I have a set enumerated type with hard coded ordinal values?

 

Edit

Change the declaration to   TEnum = (plough = 5, foo = 9, bar = 14, wtf = 1);

Now the set behaves just like the array.

Share this post


Link to post

@Uwe Raabe

Are you saying that

 

for ix := Low(Array) to High(Array)

do begin

  v := Array[ix];

 

is predictable - while

 

  for v in Array

 

is not predictable ?

 

Share this post


Link to post

Off-topic - I actually do reverse for in loops with a custom enumerator which starts on top and decrements instead.

Share this post


Link to post

That is why I wrote "strictly speaking" because there is always a difference between a formal language or syntax specification and its implementation based on its actual type.

Currently probably every language that has some kind of for-in or foreach loop of course operates an array in order but that does not mean that you can assume this for every type operable with such loops.

And when we are talking about "just for arrays" this is what is called "implementation detail".

Share this post


Link to post

The inconsistent behavior between the two variations of enumerated types is another pitfall, I guess.

Share this post


Link to post
1 minute ago, Lars Fosdal said:

Or pitfall 😛

Confusing sets with arrays because they look similar is the pitfall

 

15 minutes ago, Lars Fosdal said:

Edit

Change the declaration to   TEnum = (plough = 5, foo = 9, bar = 14, wtf = 1);

Now the set behaves just like the array.

It does not, bar and foo are still reversed 😉

Share this post


Link to post
1 minute ago, Stefan Glienke said:

It does not, bar and foo are still reversed 😉

1

Wow, I didn't notice that!  And it is not related to declaration order, nor ordinal value. That is a bit disturbing.

Share this post


Link to post
Just now, Lars Fosdal said:

Wow, I didn't notice that!  And it is not related to declaration order, nor ordinal value. That is a bit disturbing.

You should take a break - because it in fact is related to the ordinal value. The order is wtf, plough, foo, bar - but your array was wtf, plough, bar, foo

Share this post


Link to post
2 minutes ago, Stefan Glienke said:

You should take a break - because it in fact is related to the ordinal value. The order is wtf, plough, foo, bar - but your array was wtf, plough, bar, foo

Declared:   TEnum = (plough, foo, bar, wtf)
Test order: [wtf, plough, bar, foo]

Looping an Enum Set
1 wtf
5 plough
9 foo
14 bar

Looping an Enum Array
1 wtf
5 plough
14 bar
9 foo

Press Enter:

 

You are right, @Stefan Glienke -I am so logging off now 😄

  • Haha 2

Share this post


Link to post

Off-topic: There is another oddity with enumerations with explicit ordinal values.


TEnum = (plough = 5, foo = 9, bar = 14, wtf = 1000);
The above is valid, but wtf can't be used in a set.

Makes me wonder if it would be better to generally do sets as dynamic arrays, instead of today's implementation of sets.
I guess it would be more expensive.

Edited by Lars Fosdal
Corrected open array to dynamic array.

Share this post


Link to post

When used in a set, the ordinal value of the enum is basically the index of the bit used within the set and as they can only be 256 bit in size any ordinal value above 255 prevents an enum being used as a set.

Personally I think you should rather avoid giving enums any ordinal values and only ever use them when using some interop with another system where they are used for.

Edited by Stefan Glienke
  • Like 2

Share this post


Link to post
10 hours ago, Lars Fosdal said:

Makes me wonder if it would be better to generally do sets as open arrays, instead of today's implementation of sets.

What do you mean with "open arrays"? The only open arrays I know are open array parameters and I have no idea how you would implement sets with them. Did you perhaps mean dynamic arrays? If so, then please don't use confusing terminology. And it really gets confusing and troublesome if you confuse such "open arrays" with open array parameters.

 

FWIW, there is already a TBits class, implementing an indexed bitset.

Share this post


Link to post
19 hours ago, Stefan Glienke said:

When used in a set, the ordinal value of the enum is basically the index of the bit used within the set and as they can only be 256 bit in size any ordinal value above 255 prevents an enum being used as a set.

Personally I think you should rather avoid giving enums any ordinal values and only ever use them when using some interop with another system where they are used for.

Isn't there something weird about RTTI for enums that have manually set ordinal values as well?

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×