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