enums and version/static if/"inheritance"

Witold Baryluk witold.baryluk+d at gmail.com
Sun Aug 4 18:03:02 UTC 2024


On Thursday, 1 August 2024 at 17:46:37 UTC, Walter Bright wrote:
> If you really want conditional enums, you can do this:
>
> ```
> struct E
> {
>     enum A = 3;
>     enum B = 4;
>     version (XYZ) enum C = 5;
> }
>
> E e = E.A;
> ```

```
e2.d:8:7: error: cannot implicitly convert expression ‘A’ of type 
‘int’ to ‘E’
     8 | E e = E.A;
```

Also fails to catch other issues:

```d
auto e = E.A | 7;
```

Compiles, but I would prefer it didn't in this case.


The closest I was able to get to something decent (using version 
in this example is trivial, so only showing inheritance and flag 
manipulation) is this example:

```d
struct Typedef(T, T init = T.init, string cookie = "", alias 
inherit_from = T) {
   private T payload = init;

   this(T init) {
     payload = init;
   }

   this(typeof(this) tdef) {
     this(tdef.payload);
   }

   static if (!is(inherit_from : T)) {
     this(inherit_from tdef) {
       this(tdef.payload);
     }
   }

   auto ref opBinary(string op, this X, B)(auto ref B b) if (op != 
"in") {
      return Typedef(mixin("payload "~op~" b.payload"));
   }

   //static if (!is(inherit_from : T)) {
     auto ref opBinaryRight(string op, this X)(auto ref 
inherit_from b) {
       return Typedef(mixin("b.payload " ~ op ~ " payload"));
     }
   //}

   //auto ref opCast(T, this X)() {
   //  return cast(T)payload;
   //}
}

struct MMap {
   alias Flags = Typedef!(ulong, 0, "MMap");

   static const Flags None = 0;

   static const Flags A = 1;
   static const Flags B = 2;
};

struct LinuxMMap {
   alias Flags = Typedef!(ulong, 0, "LinuxMMap", MMap.Flags);

   static foreach (x; __traits(allMembers, MMap)) {
     static if (x != "Flags") {
       mixin("static const LinuxMMap.Flags "~x~" = MMap."~x~";");
     }
   }

   static const Flags C = 4;

version (X86_64) {
   static const Flags D = 30;
   static const Flags E = 34;
} else version (AArch64) {
   static const Flags F = 33;
   static const Flags G = 36;
} else {
   static assert(0);
}

}

auto n(T)(T flags0) {
   LinuxMMap.Flags flags = flags0;
   import std;
   writefln("%s %d", flags, flags.payload);
}

void main() {
   n(MMap.A | MMap.B | LinuxMMap.C);
}
```


The "inheritance" part can be easily wrapped in a reusable mixin 
template.

It detects (and rejects) constructs like `n(MMap.A | 5)`, etc.

But still accepts `n(5)`, but this is fixable using some template 
constraints.

So of course this works, and I knew it even before sending 
initial , but it is just quite ugly.


I also did check generate assembly code, and it looks good (all 
inlined, and folded to a constant `7` above), at least using gdc.


More information about the Digitalmars-d mailing list