It would be nice to extend enumarations by introducing the step keyword and binary enumerators. I'm basing this proposal on how enumerations are implemented in the PureBasic language, so I'll just paste a few examples taken from its documentation:
https://www.purebasic.com/documentation/reference/enumerations.html
The step keywords would allow to adjust the step for each new constant found in the enumeration:
Enumeration 20 Step 3 #GadgetInfo ; will be 20 #GadgetText ; will be 23 #GadgetOK ; will be 26 EndEnumeration
Binary enumarators are practical for working with flags:
EnumerationBinary #Flags1 ; will be 1 #Flags2 ; will be 2 #Flags3 ; will be 4 #Flags4 ; will be 8 #Flags5 ; will be 16 EndEnumeration
I've found these enumeration variants quite practical to use, so I was wondering if something similar could be added to Nim core language.
From what I gathered from the Nim documentation, currently this can only be achieved by manually specifying the step values for each ordinal value field — i.e. unless I'm missing out some obvious way to do this.
I dont like the EndEnumeration on Nim.
Maybe the step can be a pragma? {. step: 3 .}
All your issues seems to be mostly relevant when using extern C libs with wrappers. For this case there are two more issues, Nim enums can behave strange when they have holes or start not by zero. I was struggling with that for gintro, but we know that Araq recommends using plain (distinct) int constants for this case.
For your flags example, there we can use Nim's set[enum], which works fine, I used that approach in gintro.
@juancarlospaco:
I dont like the EndEnumeration on Nim.
these are just examples pasted verbatim from another language, the focus is on how these keyword could simplify automatic steps in the enumeration values. I wouldn't like the enum closing keyword either (I prefer indentation too).
@Stefan_Salewski
All your issues seems to be mostly relevant when using extern C libs with wrappers.
Yes, that's the case; and probably these proposed enumerations features are going to be most useful when writing wrappers and interfacing with external libraries. Especially when the enum elements are numerous, and inserting a new element in the middle would require rewriting all values manually (whereas autogenerated values via steps or binary enums would require no manual fixes.
Nim enums can behave strange when they have holes or start not by zero. I was struggling with that for gintro, but we know that Araq recommends using plain (distinct) int constants for this case.
I've read about it, but I guess that it's unavoidable when working with external libraries, some times. The overall idea is to make the syntax friendlier when this is needed. But if this was to be counter to the general Nim philosophy I'd understand it.
But I wonder then, aren't enums supposed to be helpful also for working with bit flags? so you can AND and OR together when handling settings parameters? In this case you can't avoid leaving holes in the enumeration. From what I understood from reading the documentation (and I might be wrong) the issue with having holes in enums is that you can't use some iterators with them if you do have holes.
Again, it might then be good to have a special binary enumerator type, which would not consider them as holes but expect naturally the progression to be that of bit values (1, 2, 4, etc.), without loosing functionality like iterators, etc. This could be useful in many contexts, so it might be worth to have in core.
For your flags example, there we can use Nim's set[enum], which works fine, I used that approach in gintro.
Could you please provide a link to gintro, so I can look at your example in its context for inspiration? thanks.
But I wonder then, aren't enums supposed to be helpful also for working with bit flags? so you can AND and OR together when handling settings parameters? In this case you can't avoid leaving holes in the enumeration. From what I understood from reading the documentation (and I might be wrong) the issue with having holes in enums is that you can't use some iterators with them if you do have holes.
Nim's version of bit flags is set[T], which is why enum with holes is not needed here.
Could you please provide a link to gintro,
gintro generates the Nim modules during install, you may try
nimble install gintro
For example something like
$ grep -A9 "EventFlag" gdk.nim
EventFlag* {.size: sizeof(cint), pure.} = enum
ignoreThisDummyValue = 0
exposure = 1
pointerMotion = 2
pointerMotionHint = 3
buttonMotion = 4
button1Motion = 5
button2Motion = 6
button3Motion = 7
buttonPress = 8
EventMask* {.size: sizeof(cint).} = set[EventFlag]
is generated. For this case it was necessary to insert a virtual flag 0, but it is an exception, most Flags start with bit 0. Using a set allows passing {pointerMotion, buttonPress} as function arguments, no strange or constructs are needed.
One additional restriction of Nim's enums is, that values must all be different.
So this is not allowed:
$ grep -A9 "EventFlag" gdk.nim
EventFlag* {.size: sizeof(cint), pure.} = enum
ignoreThisDummyValue = 0
exposure = 1
pointerMotion = 2
mouseMotion = 2
pointerMotionHint = 3
buttonMotion = 4
button1Motion = 5
button2Motion = 6
button3Motion = 7
buttonPress = 8
EventMask* {.size: sizeof(cint).} = set[EventFlag]
But we can do
const mouseMotion = EventFlag.pointerMotion
if an alias is really needed. (Generally it is not needed.)
@leorize:
Nim's version of bit flags is set[T], which is why enum with holes is not needed here.
Ah, I had missed that! Indeed, this is a cleaner approach than ORing and ANDing. Maybe a binary enum could be added as syntactic sugar to define the same in a less verbose manner then (at least, for single byte flags).
@Stefan_Salewski: thanks for the exhaustive examples, they're really helpful.
You might want to check out nimterop's rendering of C enums. Initially, it generated a regular Nim enum but due to various restrictions, we now use distinct ints instead and it works quite well.
For example: soloud_h.c looks like this.
This method supports holes, out of order and repeat instances. This is especially useful with C enums that use bit shifts which can easily be out of order.
types.nim makes this possible.