bmx-ng / bcc

A next-generation bcc parser for BlitzMax
zlib License
33 stars 13 forks source link

Enums: Get "index" or "length" ? #600

Open GWRon opened 1 year ago

GWRon commented 1 year ago

Hey,

as @scaremonger just wrote about it on discord ...

SuperStrict
Enum WEEKDAY; MON,TUE,WED,THU,FRI,SAT,SUN; End Enum

Local today:WEEKDAY = WEEKDAY.MON
Print "Today is "+today.toString()

Local tomorrow:WEEKDAY = WEEKDAY( ( today.ordinal() + 1 ) Mod 7 )
Print "Tomorrow is "+tomorrow.toString()

Local yesterday:WEEKDAY = WEEKDAY( ( today.ordinal() + 6 ) Mod 7  )
Print "Yesterday was "+yesterday.toString()

How to retrieve the "index" of an enum element, or the total amount of elements in an enum?

So in this case:

print WEEKDAY.length 'should print 7
print WEEKDAY.MON.index 'should print 0

Would that be ... doable ... and would that be ... good to have?

For now we would have to rely on WEEKDAY.Values() (array) to retrieve the length, and today.ordinal() would return the numeric value (so when starting from "0" and not "sparse", it could work as array index).

woollybah commented 1 year ago

An index would be limited in its usefulness, but I suppose there'd be no reason to not have it.

GWRon commented 1 year ago

The index is maybe only useful in connection with values() (arrax) and .length Dunno what other benefits it could provide.

HurryStarfish commented 1 year ago

I can't really think of a use outside of reflection either. But if you do need it and Values() exists, you can also do this yourself by iterating a enum and building a map, right?

GWRon commented 1 year ago

I did not check if "Values()" creates a new array each time..or returns a predefined one.

So all the fuzz is more or less based on that code by scaremonger (ex. Finding the "next" enum to a given one). Iterating over arrays might not be the fastest one. Also given: enums exist during compile time, so stuff can be done in that step already, compared to runtime-array-traveling.

HurryStarfish commented 1 year ago

iirc it creates a new array every time right now, because you can modify it. But if we had/once we have properly working generics, it would make more sense for it to return an IIterable instead and would no longer need to do that.

Also given: enums exist during compile time, so stuff can be done in that step already, compared to runtime-array-traveling.

But does that matter? It would only have to be done once at program start.

GWRon commented 1 year ago

Also given: enums exist during compile time, so stuff can be done in that step already, compared to runtime-array-traveling.

But does that matter? It would only have to be done once at program start.

I was talking about pieces of code which execute for local e:MyEnum... EachIn MyEnum.Values() or similar. If the array was recreated each time, it creates an object over and over (each time it reaches that for loop) which isn't necessary.

So it might be better if it creates and populates the array (which Values() would return) during compilation already.

HurryStarfish commented 1 year ago

Yeah, if Values() returned a read-only implementation of IIterable, it would only need to be created once. Right now though, it returns an array which you can modify, so it has to create a new one every time to prevent you from being able to break things.

GWRon commented 1 year ago

Hmm ok ... so meanwhile it could return myPrecalculatedArray[..] - so a simple array copy instead of an array which it fills during runtime.

GWRon commented 1 year ago

@Scaremonger By chatting with @davecamp I saw him using something which you could kinda use as ".length" alternative. He simply added a "LastID" at the end of the enum.

Enum EWeapons
    Sword
    Axe
    Dagger
    LastID
EndEnum

Knowing that "LastID" will be existing in the enum it allows you to catch how many other elements are in there.

But of course it comes with pitfalls:

HurryStarfish commented 1 year ago

Why not just EWeapons.Values().length?

(on an unrelated note, non-flags enums should be singular rather than plural; every value of the type only represents a single weapon, so EWeapon makes more sense than EWeapons)

GWRon commented 1 year ago

as Values() creates a new array just to retrieve the length ... while "lastID" is populated on compile time.

Regarding enum name - I just made up the example without looking at grammar, but you are right here.

BTW: you could surely also just have global EWeaponCount:Int = 3 right below the enum - of course you need to adjust it then each time. the "LastID" variant needs "write access" to the enums - which is not always given. I just added it to here as I think it was an interesting "trick".

HurryStarfish commented 1 year ago

as Values() creates a new array just to retrieve the length ... while "lastID" is populated on compile time.

It currently does, yeah, but the enum isn't gonna change at runtime, so if performance is a problem you can get the array (or the length) once at startup and keep it in a Global somewhere. Global WeaponCount:Int = EWeapon.Values().length is just one extra line, and you don't have to adjust it when you change the enum.

Scaremonger commented 1 year ago

As an enum is a fixed size anyway; I would use a constant instead of a global, but doesn't that kind of defeat the objective of using enums instead of constants in the first place?