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