Defining an enum
type whose members have specific bits set is a common task. Therefore, the language should make that easy to do correctly and hard to do incorrectly.
The main purpose is to make defining and maintaining flags correctly easy. In particular, for normal enums, adding flags anywhere except the end is error prone, as subsequent values need to be updated. In the
I propose to add an attribute @flags
that can be applied to enum
types only.
A @flags enum
only different from normal a normal enum on the definition side.
In particular on the definition side:
- It must have some unsigned integer type as its underlying type, the default is
uint
. - It is an error to have the first member unassigned. This is because some flag enums have a neutral element with value
0
, and for others, that makes no sense and they have the first member with value1
. The language should not make assumptions; requiring the programmer to write= 0
or= 1
explicitly is not a big ask. - It is an error to assign
0
to any member except the first. - If the first member is
0
, there must be at least one other member,
and the second member must be unassigned, which gives it the value1
. - Members with explicit values must have an
OrExpression
of pairwise different, previously defined constants (possibly just one constant). - Members without initializers, generally speaking, progress in powers of 2.
- As a special exception, the last member may be assigned the underlying type’s
max
(either asuint.max
or-1
), for the purpose of expressing an invalid non-zero value.
In particular, members without initializers follow these rules:
- If it is the second member and the first member has value
0
, it has value1
. - If the previous member is has no initializer, it has value double the previous member.
- If the previous member has a non-zero non-power-of-2 value, consider the member before that.
All in all, this means that power-of-2 members have their values implicitly assigned, except for possible doppelgängers, and members with an initializer are ignored for the purpose of value progression as their purpose is to be an abbreviation for prepackaged options.
If a flags enum has an invalid value, it is its init
; otherwise the init
is zero, even if a neutral option does not exist.
@flags enum WindowOptions : ubyte
{
empty = 0,
titleBar, // 1
statusBar, // 2
progressBar = statusBar, // doppelgänger
minimizeButton, // 4
maximizeButton, // 8
closeButton, // 16
standardButtons = minimizeButton | maximizeButton | closeButton, // prepackaged combo
defaultButtons = standardButtons,
helpButton, // 32
dialogButtons = closeButton | helpButton,
allButtons = defaultButtons | dialogButtons, // closeButton overlaps, but okay
invalid = -1
}
@flags enum Options : ubyte
{
invalid = -1, // error, can only start with 1 or 0, -1 goes to the end
b = 2, // error: can’t skip 1
c = 6, // error, can’t assign values directly
}