Flags enum

monkyyy crazymonkyyy at gmail.com
Tue Jul 2 17:50:13 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
> }
> ```

```d
struct flag{
     int i=1; alias i this;
     auto opBinary(string s:"+")(int a){
         assert(a==1);
         return flag(i*2);
     }
}
enum foo{
     a=flag(1),b,c,d,e
}
void main(){
     foo f;
     f=cast(foo)(foo.e|foo.c);
     import std;
     f.i.writeln;
}
```
good luck getting such a thing merged into phoboes but this could 
be a lib solution


More information about the dip.ideas mailing list