enums.md 8.0 KB


id: enums title: Enums

sidebar_label: Enums

Introduction

An Enum is an efficient way to define a set of constants that can be assigned to variables. Say, for example, you'd like to represent the days of the week. Since this is a fixed set of seven possible values, you could define those values within an Enum :

Enum EDay
	Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
End Enum

Comma separators are optional, and elements can be placed on separate lines.

Enum ESequence
	Morning
	Afternoon
	Night
End Enum

By default, the underlying type of an enum element is an Int, but you can specify a different numeric type by using the standard type declaration syntax, as shown in the following example. Valid integral numeric types that can be assigned as a type include, Byte, Short, Int, UInt, Long, ULong and Size_T.

Enum EMonth:Byte
	Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
End Enum

When you do not specify any values for the elements, the default is to assign it the index in the list of elements, starting from 0. So, from the EDay example, EDay.Sunday is associated with the value 0, EDay.Monday with 1, and so on.

Using Enums

You assign an enum value to a variable just as you would any other type:

Local meetingDay:EDay = EDay.Monday

A variable declared as a particular type of Enum can only be assigned to enum elements of that kind.

meetingDay = EDay.Wednesday ' good

meetingDay = EMonth.Aug ' error
meetingDay = 3 ' error

To retrieve the numeric value for a particular enum element, you can use the Ordinal() method:

Print EMonth.May.Ordinal()

Would print 4.

The ToString() method returns a String representation of the enum element:

Print EMonth.Sep.ToString()

Would print Sep.

The Values() function returns an array of all elements:

For Local month:EMonth = Eachin EMonth.Values()
    Print month.ToString() + " = " + month.Ordinal()
Next

Assigning Values

Rather than using just the default values, you can also assign any constant value (or something that will evaluate to a constant value at compile time) to the elements of an Enum.

Enum EMachineState
	PowerOff = 0
	Running = 5
	Sleeping = 10
	Hibernating = Sleeping + 5
End Enum

Subsequent elements that do not specify a value, increment from the preceding entry. So if the preceding element had a value of 15, the next would automatically have the value 16.

Bit Flag Enums

You can use an Enum to define bit flags, which enables an instance of the Enum to store any combination of the values that are defined in its elements.

You create a bit flags enum by applying the Flags modifier to the Enum declaration, defining the values appropriately so that standard bitwise operations can be performed on them.

In the following example, an Enum called EDays is defined with the Flags modifier. Each value is automatically assigned the next greater power of 2, starting at 1. This enables you to create an EDays variable whose value is EDays.Tuesday | EDays.Thursday.

SuperStrict
Framework brl.standardio

Local meetingDays:EDays = EDays.Tuesday | EDays.Thursday
Print meetingDays.ToString()

Enum EDays Flags
	Sunday
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
End Enum

To set a flag on an enum, use the bitwise or operator (|) as shown in the following example:

' Initialize with two flags using bitwise Or.
meetingDays = EDays.Tuesday | EDays.Thursday

' Set an additional flag using bitwise Or.
meetingDays :| EDays.Friday

Print "Meeting days are " + meetingDays.ToString()
' Output: Meeting days are Tuesday|Thursday|Friday

' Remove a flag using bitwise XOr.
meetingDays :~ EDays.Tuesday
Print "Meeting days are " + meetingDays.ToString()
' Output: Meeting days are Thursday|Friday

To determine whether a specific flag is set, use a bitwise AND operation (&), as shown in the following example:

' Test value of flags using bitwise AND.
Local test:Int = (meetingDays & EDays.Thursday) = EDays.Thursday
If test Then
	Print "Thursday is a meeting day."
Else
	Print "Thursday is not a meeting day."
End If
' Output: Thursday is a meeting day.

Bit flag enums can also have their values specified. It is generally advised to make the values powers of 2, so that you can apply bitwise operations to them. Default values for elements will always be a power of 2, the next element being the next logical power of 2 higher than the previous entry.

SuperStrict
Framework brl.standardio

For Local attackType:EAttackType = EachIn EAttackType.Values()
	Print attackType.ToString() + " = " + attackType.Ordinal()
Next

Enum EAttackType Flags
    Melee
    Fire
    Ice = $8
    Poison
End Enum

Because bit flags are limited to the number of bits for a given type (e.g. 8 bits for a Byte, 32 bits for an Int), you need to ensure that the type you use for a bit flag Enum has enough bits for all the elements you require.

If you try to use more bits than the type allows, the compile will fail with an appropriate error message.

Enum vs Const

As well as making the code easier to read, by using an Enum instead of a set of Const values, we can let the compiler ensure that only the specific set of values defined by the Enum be used.

In the following example, we define a set of consts to record the types of available tyres a car can use:

SuperStrict
Framework brl.standardio

Local car:TCar = New TCar

TPitstop.ChangeTyre(car, TYRE_SOFT)

Const TYRE_SOFT:Int = 0
Const TYRE_MEDIUM:Int = 1
Const TYRE_HARD:Int = 2
Const TYRE_INTER:Int = 3
Const TYRE_WET:Int = 4

Type TPitstop

	Function ChangeTyre(car:TCar, set:Int)
		If car.tyreSet = set And set <> TYRE_WET Then
			Throw"Can't change to same kind of tyre"
		End If

		Local s:String
		Select set
			Case TYRE_SOFT
				s = "Soft"
			Case TYRE_MEDIUM
				s = "Medium"
			Case TYRE_HARD
				s = "Hard"
			Case TYRE_INTER
				s = "Inter"
			Case TYRE_WET
				s = "Wet"
			Default
				Throw "Not a valid tyre choice"
		End Select
		car.tyreSet = set
		Print "Changing to " + s
	End Function

End Type

Type TCar
	Field tyreSet:Int = TYRE_MEDIUM
End Type

We expect that users of our code will diligently use the defined constants, instead of numbers – TYRE_SOFT, TYRE_MEDIUM and so on. The problem lies in the fact that someone may decide not to use the constants defined by us and may submit an invalid number as an argument of our function, for example: -1 or 10. In this case, if the function does not check for invalid value, it will likely result in incorrect behaviour.

To avoid this problem we can replace the constants with an Enum, thereby constraining the values that can be passed into the function :

SuperStrict
Framework brl.standardio

Local car:TCar = New TCar

TPitstop.ChangeTyre(car, ETyre.Soft)

Enum ETyre
	Soft
	Medium
	Hard
	Inter
	Wet
End Enum

Type TPitstop

	Function ChangeTyre(car:TCar, set:ETyre)
		If car.tyreSet = set And set <> ETyre.Wet Then
			Throw"Can't change to same kind of tyre"
		End If

		Local s:String
		Select set
			Case ETyre.Soft
				s = "Soft"
			Case ETyre.Medium
				s = "Medium"
			Case ETyre.Hard
				s = "Hard"
			Case ETyre.Inter
				s = "Inter"
			Case ETyre.Wet
				s = "Wet"
		End Select
		car.tyreSet = set
		Print "Changing to " + s
	End Function

End Type

Type TCar
	Field tyreSet:ETyre = ETyre.Medium
End Type

Notice that we no longer need to Throw on unsupported values of set because we know that only the values specified by the Enum will ever be used.