Flags enum

Basile B. b2.temp at gmx.com
Tue Jul 9 23:13:52 UTC 2024


On Thursday, 27 June 2024 at 19:03:01 UTC, Quirin Schroll wrote:
> 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 value `1`. 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 
> value `1`.
> * Members with explicit values must have an 
> [`OrExpression`](https://dlang.org/spec/expression.html#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 as `uint.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 value `1`.
> * 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.
>
> ```d
> @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
> }
> ```
>
> ```d
> @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
> }
> ```

I don't think this is a full progress, only a half. I see that 
the idea is to make the bitsets based on enum members more safe 
but then why not introduce a native bitset type ? For example 
that propostion will not lead to lift the code of OrExp, AndExp, 
XorExp, etc.


More information about the dip.ideas mailing list