Flags enum

Nick Treleaven nick at geany.org
Wed Jul 3 12:37:13 UTC 2024


On Tuesday, 2 July 2024 at 17:50:13 UTC, monkyyy wrote:
> 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
> }

Nice!

> void main(){
>     foo f;
>     f=cast(foo)(foo.e|foo.c);

`foo.e|foo.c` is not a value of `foo`, so the cast shouldn't be 
used. However the op could be a valid `flag` with an overload for 
`|` - I renamed `flag` to `Flags` and added that below. Also:
* We can support no flags set too.
* I disallow setting `Flags` to an integer to prevent accidents, 
instead use `Flags.mask`.
* `Flags.mask(-1)` can be used as an invalid value / all bits set.
* I check that the ctor and `opBinary(1)` are not called unless 
`i` is a multiple of 2
* I check that `opBinary(1)` doesn't overflow `i`

```d
struct Flags{
     import core.bitop;

     uint i = 0;
     alias this = get;
     uint get() => i;

     // a must be a multiple of 2
     this(uint a) {
         assert(popcnt(a) == 1);
         i = a;
     }
     static Flags mask(uint a) {
         Flags f;
         f.i = a;
         return f;
     }
     // for use inside enum type after a ctor call
     auto opBinary(string op:"+")(uint a) {
         assert(a == 1);
         assert(popcnt(i) == 1);
         assert(i < 0x80000000); // avoid overflow
         return mask(i * 2);
     }
     auto opBinary(string op: "|")(Flags rhs) => mask(i | rhs.i);
     auto opBinary(string op: "&")(Flags rhs) => mask(i & rhs.i);
}

enum Foo {
     none = Flags(), a = Flags(1), b, c, d, e, m = c | e, invalid 
= Flags.mask(-1)
}

void main(){
     import std.exception;

     assert(Foo.none == 0);
     Flags f = 2;
     assertThrown!Error(f = Foo(3)); // >1 bit set
     assertThrown!Error(f = Foo.none + 1); // 0 bits set
     assertThrown!Error(f = Foo.m + 1); // >1 bit set
     assertThrown!Error(f = Foo.mask(0x80000000) + 1); // overflow

     static assert(!__traits(compiles, f = 5));
     assert(Foo.m == 20);
     assert(Foo.invalid & Foo.b);
     assert((Foo.e & Foo.c) == 0);
     assert(Foo.m & Foo.c);
}
```


More information about the dip.ideas mailing list