Frustration with Enumerations (yet again)

By now I should know better, but more often than care to admit, Enumerations in Xojo come back to bite me in the behind. Let me elaborate…

Even though they appear (and even claim) to be, as they are currently implemented, Enumerations in Xojo are do not consist of values of a fundamental type (i.e. Integer, or String). This often leads to the programmer casting enums into one of the standard variable types, making for much less readable code, exactly opposite of what they are designed to accomplish.

For example, I am writing a class to perform Histogram adjustments on an image. In general these consist of white point, midpoint and black point level modifications, using either the Red, Green, Blue or Intensity (averaged) channels of a picture object (if you’re confused, take a look at the Histogram tool in the image editor of iPhoto). I need to keep track of these three values for each of the four channels, so building enumerations like so:

Public Enum HistogramValues As Integer
  MinPoint = 0
  MidPoint = 1
  MaxPoint = 2
End Enum

Public Enum HistogramChannels As Integer
  Intensity = 0
  Red = 1
  Green = 2
  Blue = 3
End Enum

seems reasonable. This would allow keeping the actual level values in a 2 dimensional array called “Level” (actually since Xojo can’t handle Properties with parameters, we need to code this as separate Methods with the same name, one using the “Assigns” keyword, keeping the actual level values in a shadow array).


Dim mLevel(2, 3) As Integer

Function Level(Value As HistogramValues, Channel As HistogramChannels) As Integer
  Return mLevel(Value, Channel)
End Function
Private Sub Level(Value As HistogramValues, Channel As HistogramChannels, Assigns Level As Integer)
  mLevel(Value, Channel) = Level
End Sub

Alas, this doesn’t actually work, because Enumerated values can’t be used as indices in the array lookup (even though they are defined as “integers”, the compiler doesn’t think of them as such).

Sure we can cast them as integers (and that’s what most people do):

Function Level(Value As HistogramValues, Channel As HistogramChannels) As Integer
  Return mLevel(Integer(Value), Integer(Channel))
End Function
Private Sub Level(Value As HistogramValues, Channel As HistogramChannels, Assigns Level As Integer)
  mLevel(Integer(Value), Integer(Channel)) = Level
End Sub

but in my case, this is real time video, so this unnecessary “casting” is happening 30 times per second.

Answer is simple: get rid of all the value checking that occurs on enumerated variables at run time. Treat them as standard variable types (Integers or Strings) and allow us to use them the way they were intended (as a means of helping the programmer write more readable code), not as some weird user defined “pseudo-variable-type”.

The sad thing is that I’ve had this conversation before, and each time lots of really good Xojo programmers (who I highly admire) are quick to second my exasperation, but year after year nothing changes. I don’t think that changing this would break anybody’s code, but I absolutely know that it would make using Enumerations much more useful.

At least think about it.

Cheers.

-bill k

Sympathy, and I agree with you.

But… since this is your own code, aren’t you forcing this on yourself?
If you make your functions use integer parameters, then no casting is required.

Instead of enums, use constants

HistogramValues_MaxPoint = 2

All you lose is the bounds checking that an enum would be giving you, unless Ive missed something , as Im sure you considered this?

Enums are not just a list of integers, the exact underlying type is what you specify.
They ARE a type in and of themselves just like classes are.
Not auto converting to/from integer is deliberate - so YOU have to think about it and code for it.

Second - use CType - it seems to perform much quicker and closer to having to do nothing - almost a no-op
Return mLevel(Ctype(Value,Integer), CType(Channel, Integer))

huh? I though you could still only use Integers as enums! I also thought you had to cast them to Uint32… I guess I missed something.

I use enums a lot in my apps, would love to be able to use strings!

On a slightly different note, are you building for OS X only? If so, Core Image (which is awkward to start with, but ultimately worth your time) has histogram functions in it, and with 10.10 Apple also provide a histogram viewer.

if not, don’t forget that Xojo has the map functions of the RGBSurface which can be used as a levels tool.

I just timed this and found no real time difference between an empty loop, casting 2 enums in the loop and ctyping 2 enums, but calling a single method adds 70%. These values are eyeball-averaged from a built app with other apps running so they’re rough and would fluctuate 5% or so…

no pragmas
empty/ctype/cast 38000
foo() 65000

pragmas on
empty 4000
ctype/cast 6000
foo() 35000

I wouldn’t be too concerned about a simple method that’s only called 30 (or even 30*12) times a second but if you’re really looking for speed then ditch the level method (as that’s the limiting factor) and use a memoryblock in place of mLevel, retrieve a Ptr to it and write the values all in one go. erm, I’m being facetious here :slight_smile: I doubt you’d notice a speed difference.

Jeff: Yes, using constants is a completely viable approach, but it does loose the advantage of code hinting. Not a deal breaker, but unfortunate nonetheless.

Norman: Your point is exactly what’s wrong with Enums in Xojo. I can’t think of another language that treats them this way. I’d like to see the object stack that results from the two approaches (casting vs. CType), although it’s almost certainly a moot argument.

Sam: I misspoke (typed?) when it came to “Strings”. Even though you can put whatever you want as the “Type” field in Enumerations, it always get’s set back to “Integer” (which begs the question: Why even bother with the “Type” field). Unfortunately the application is cross-platform, so (at least for now) brute force level calculations are what I’ll be doing. I’ll have to look closely at the RGBSurface options. I appreciate the tip.

At the very least, C++11’s scoped enumerations behave like Xojo’s.

The type field is the enumeration’s underlying type, which is useful when putting them in structures where the layout is dictated by code that you have to interoperate with.

It’s almost as though they are sort of integers, but not really. I can test if one enum_variable is less than another, but I can’t test if one is less than an integer. I can add them (only to each other) and even multiply them (only to each other), which is almost certain to result in a value which is not in the enumerated list, but I can’t do something as simple as performing bitwise operations on them.

If they’re Integers, treat them like integers, if they’re strings, treat them like strings. Please explain the inherent advantage - from a programmers standpoint - to treat them as their own unique variable type that can only [really] be passed or compared.

[quote=133516:@William Koperwhats]It’s almost as though they are sort of integers, but not really. I can test if one enum_variable is less than another, but I can’t test if one is less than an integer. I can add them (only to each other) and even multiply them (only to each other), which is almost certain to result in a value which is not in the enumerated list, but I can’t do something as simple as performing bitwise operations on them.

If they’re Integers, treat them like integers, if they’re strings, treat them like strings. Please explain the inherent advantage - from a programmers standpoint - to treat them as their own unique variable type that can only [really] be passed or compared.[/quote]

Enums ARE a new type when you create them. They are NOT just some special code for handling integers.
They are a type that CAN convert to / from integers (like you can do with strings where you have to convert them)

Because they are a new type with a limited range of values (the enumeration member values) you CAN write code like

Enum Speed
   slow
   medium
   fast
   faster
   warp
end enum

sub setSpeed( new speed as speed )
end sub

setSpeed( 65 )

and get a compiler error saying 65 is the wrong type

For how you expect to deal with the you should define a bunch of constants and use those
You’ll have the behavior you expect

First, why in the world does the IDE allow use to type anything into the Enum type field nit then ONLY allow integer types? That is awfully unintuitive.

If enum data type is restricted to just integer types should not there just be a popup menu with those choices?

I agree it is a REAL pain NOT to be able use them directly for things like indices of arrays…
It sure would help with readable code as they really do help ORGANIZE things with less clutter than doing it with constants…

But my biggest issue with Enums is that you can not use them in the inspector. The pseudo enums in the inspector definitions can not be used as Enums in code as well as the other way around.

Maybe we could use something like a “Named Constant Group” which would be defined like enums in the IDE, anywhere enums can be with the same scope options. It’s members would be addressed by dot notation but who’s type can be any basic type and for which the complier treats each member as a constant of that type.

With the navigator we really don’t want to have long lists of constants… there is too much visible at once as it is!

The Navigator is one reason I have been trying to use Enums instead of constants - which is not exactly their purpose as Norm points out. A “Named Constant Group” feature would really help.

But this is something I doubt Xojo Inc would ever consider…

I suspect they would see it as too similar to Enums or violating some good programming principle, or simply redundant and never high enough priority to do…

But with the navigator, anything that helps keep the clutter down is very valuable!

I forgot to mention … Of course if a “Named Constant Group” feature was ever implemented I would want to be able to use them in the inspector as well so the value can be chosen from a menu in the inspector.

As Norman states, when you create a new enum, you are creating a new type. That is the key thing to get your head around when using enums. They are NOT integers. They are NOT a collection of integers. They are a new type. Just like creating a class interface creates a new type, creating enums creates a new type.

For example, I use enums to set what sort of value I want to do in a thread. I use Alex Restreppo’s thread pool classes in my projects very heavily. In his class, you send an object into a thread and use a class interface method as where you do the thread’s work. Since the object is passed generically into the thread pool, you need to be able to specify what to do in that class interface method. So I use enumerations. So I have several that are like this. My object is Device, my enum is WorkTasks and I have the following values:

Device.WorkTasks.Login
Device.WorkTasks.UpdateIPAddress
Device.WorkTasks.DoFirmwareUpdate
Device.WorkTasks…StartImagePull

etc.

Whenever I want to do a specific action, I first set the value of my enumeration to what I want to accomplish. Then the case statement in my thread class interface method launches other methods or functions based on the enumeration value. This way the code is very easy to read, it’s not prone to mistakes if I was using strings instead, etc.

I don’t even think or worry about what the value of my enum is. It’s a TYPE and I use it as a TYPE not as a way to organize data. For that I use arrays, dictionaries or classes.

I understand the reasoning behind this, but by that logic the following should also not compile:

setSpeed(Speed.slow + Speed.warp)
Dim newSpeed As Speed = Speed.Fast * Speed.Faster

…and it most certainly will. Who knows what will happen when it runs (most likely an error), but the compiler doesn’t care.

So [facetiously] the compiler either needs to completely disallow all numeric operations on Enums (if this is how you want to treat them), or [preferably] give up and treat them like “named” integers, with the advantage of dot notation and code hinting. Based on the comments above, it seems fairly obvious that the clear preference (other than the Xojo engineers) is for the latter.

[quote=133528:@Karen Atkocius]First, why in the world does the IDE allow use to type anything into the Enum type field nit then ONLY allow integer types? That is awfully unintuitive.
[/quote]
Bug

That bug has been fixed for a future release

You can’t use them as indexes for an array immediately because indexes are INTEGERS not “MyEnumeratedTypeThings”

For indexes use Ctype( enumeratedValue, Integer) - that way its VERY clear & explicit what you’re doing & not just some magic thats hidden away that you DIDN’T intend

Its one reason I encourage people to avoid variants - lots of hidden magic and when you depend on that magic and it DOESN’T do what you want or expect you’re out of options

[quote=133532:@William Koperwhats]I understand the reasoning behind this, but by that logic the following should also not compile:

setSpeed(Speed.slow + Speed.warp)
Dim newSpeed As Speed = Speed.Fast * Speed.Faster

…and it most certainly will. Who knows what will happen when it runs (most likely an error), but the compiler doesn’t care.[/quote]
The compiler currently doesn’t
Thats a bug as far as I’m concerned
Future versions may actually care & make your code not compile

[quote=133539:@Norman Palardy]You can’t use them as indexes for an array immediately because indexes are INTEGERS not “MyEnumeratedTypeThings”

For indexes use Ctype( enumeratedValue, Integer) - that way its VERY clear & explicit what you’re doing & not just some magic thats hidden away that you DIDN’T intend

[/quote]

I understand they are a type. I’ve understood types for decades from back when I fought myself Pascal.

But for enums, while either casting Or CType makes the intent VERY explicit, it makes the code LESS HUMAN readable than it could be…

That is why I would like Name Constant Groups.

[quote=133543:@Karen Atkocius]I understand they are a type. I’ve understood types for decades from back when I fought myself Pascal.[quote]
So it should’t be a surprise when you can use an ENUM where an INTEGER is required

[quote=133543:@Karen Atkocius]But for enums, while either casting Or CType makes the intent VERY explicit, it makes the code LESS HUMAN readable than it could be…
[/quote]
I’d disagree - yes it adds more letters BUT it also clearly exposes your intent
And understanding the intent is at least as important as being able to parse the characters
Actually after working on the IDE for 6 years I’d say WAY MORE important

[quote=133546:@Norman Palardy]'d disagree - yes it adds more letters BUT it also clearly exposes your intent
And understanding the intent is at least as important as being able to parse the characters
Actually after working on the IDE for 6 years I’d say WAY MORE important[/quote]

I don’t understand…
We are not talking about variants that can be any type in either case…

Yes I understand your argument that Enums can only take certain values so that shows intent…

But that is NOT how the language is defined… An Enum variable CAN take any value consistent with it’s integer type, not just the ones in the enumeration …

And No that is not a bug as they were designed that way on purpose. In fact when they were introduced I argued that SHOULD be the case but a REAL engineer (can’t remember who anymore) gave a reason why it was not designed that way…

Given that, and the fact enums can only be integers using them as integers does not introduce ambiguity as variant would. And the less naturally and easily readable a line of code is at a glance the more a language gets in the way… (Which is why I don’t do C like languages)

That said, I don’t care if you change it so an Enum variables can only take on the values in the definition… As long as there were able to “Named Constant Groups”. You could think of those as common or special values for something that can take on other values as well and this… In any case they would show intent to teh same degree as using constant does now, except that the group would show that they are related (and help clean up the navigator!).

[quote=133663:@Karen Atkocius]I don’t understand…
We are not talking about variants that can be any type in either case…

Yes I understand your argument that Enums can only take certain values so that shows intent…

But that is NOT how the language is defined… An Enum variable CAN take any value consistent with it’s integer type, not just the ones in the enumeration …

[/quote]

You are still trying to think of enums as a special type of integer or way to organize integers. Don’t think that way. If you do, it will trip you up every time. As an enum is its own new type, the integer “value” of a particular enum really should not matter. If you use it as its own type, then you won’t have a problem.