www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - CTFE & enums & static assert

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
I find this a bit strange:

// get all rules that start with p...
enum BolRules = StaticFilter!(beginsWithP, __traits(allMembers,BolSource));
static assert(is(BolRules == enum));

Compiling using dmd...
source/app.d(114): Error: static assert  (is(BolRules == enum)) is false

I'm trying to construct an enum that can be used in a "final switch" to 
check if all cases were handled. But so far it's not working, because I 
can't get the enum constructed.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
May 04 2015
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Mon, 04 May 2015 18:21:59 +0200, Robert M. M=C3=BCnch wrote:

 I find this a bit strange:
=20
 // get all rules that start with p...
 enum BolRules =3D StaticFilter!(beginsWithP,
 __traits(allMembers,BolSource));
 static assert(is(BolRules =3D=3D enum));
=20
 Compiling using dmd...
 source/app.d(114): Error: static assert  (is(BolRules =3D=3D enum)) is fa=
lse
=20
 I'm trying to construct an enum that can be used in a "final switch" to
 check if all cases were handled. But so far it's not working, because I
 can't get the enum constructed.
that's due to `enum` keyword abusing. "enum type" is something like this: enum MyEnum { A, B } and enum val =3D false; is a compile-time boolean constant, which will be inlined on using. i.e.=20 compiler will inline such "enum constants", and that constants has the=20 corresponding type, they aren't enums.=
May 04 2015
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2015-05-04 17:21:34 +0000, ketmar said:

 that's due to `enum` keyword abusing. "enum type" is something like this:
 
   enum MyEnum { A, B }
 
 and
 
   enum val = false;
 
 is a compile-time boolean constant, which will be inlined on using. i.e.
 compiler will inline such "enum constants", and that constants has the
 corresponding type, they aren't enums.
Hmm... Ok, I understand that these seems to be two different things. Not sure if I now understand this correct then: enum A {a, b, c}; enum members1 = __traits(allMembers, A); auto members2 = __traits(allMembers, A); pragma(msg, typeof(members1)); pragma(msg, typeid(members1)); pragma(msg, typeof(members2)); pragma(msg, typeid(members2)); Gives this: (string, string, string) playground.d(9): Error: no type for typeid(members1) playground.d(9): while evaluating pragma(msg, typeid(members1)) (string, string, string) playground.d(12): Error: no type for typeid(members2) playground.d(12): while evaluating pragma(msg, typeid(members2)) 1. So the (string, string, string) output is based on the expression of members1? Meaning, the right-hand-side. 2. I'm wondering why members1 doesn't has a type at all. So, we don't have a compile-time boolean? 3. For members2, where auto is used, I would expect that the return type of "allMembers" is used. Which would be a tuple. But again, no type for members2. Which I find quite strange. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
May 04 2015
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 05/04/2015 11:07 AM, Robert M. Münch wrote:

 enum A {a, b, c};

 enum members1 = __traits(allMembers, A);
 auto members2 = __traits(allMembers, A);
 1. So the (string, string, string) output is based on the expression of
 members1? Meaning, the right-hand-side.
There are many different kinds of tuples in D, only two of which I can handle: 1) Tuple from std.typecons, which are ordinarily created at run time 2) TypeTuple from std.typetuple, which are compile-time entities The documentation is not clear that __traits(allMembers) returns a TypeTuple, which additionally has a bad name. TypeTuple is great because it enables "compile-time foreach" (unfortunately, implicitly): foreach (m; __traits(allMembers, A)) { // This body is repeated for each member at compile time } It is also possible to expand members of a TypeTuple just by putting it inside an array: [ __traits(allMembers, A) ]On 05/04/2015 11:07 AM, Robert M. Münch wrote:
 enum A {a, b, c};

 enum members1 = __traits(allMembers, A);
 auto members2 = __traits(allMembers, A);
 1. So the (string, string, string) output is based on the expression of
 members1? Meaning, the right-hand-side.
There are many differents kinds of tuples in D, only two of which I can handle: 1) Tuple from std.typecons, which are ordinarily created at run time 2) TypeTuple from std.typetuple, which are compile-time entities The documentation is not clear that __traits(allMembers) returns a TypeTuple, which additionally has a bad name. TypeTuple is great because it enables "compile-time foreach" (unfortunately, implicitly): foreach (m; __traits(allMembers, A)) { // This body is repeated for each member at compile time } It is also possible to expand members of a TypeTuple just by putting it inside an array: [ __traits(allMembers, A) ] However, it should be obvious that all members must have a common type to be members of the same array. (True to its name, TypeTuple can have types themselves as well.) Powerful stuff. :)
 2. I'm wondering why members1 doesn't has a type at all.
Because it is a TypeTuple of values of various types and even types themselves.
 So, we don't
 have a compile-time boolean?
I don't understand that one. :( Ali However, it should be obvious that all members must have a common type to be members of the same array. (True to its name, TypeTuple can have types themselves as well.) Powerful stuff. :)
 2. I'm wondering why members1 doesn't has a type at all.
Because it is a TypeTuple of values of various types and even types themselves.
 So, we don't
 have a compile-time boolean?
I don't understand that one. :( Ali
May 04 2015
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2015-05-04 18:22:17 +0000, Ali Çehreli said:

 There are many different kinds of tuples in D, only two of which I can handle:
 
 1) Tuple from std.typecons, which are ordinarily created at run time
 
 2) TypeTuple from std.typetuple, which are compile-time entities
 
 The documentation is not clear that __traits(allMembers) returns a 
 TypeTuple, which additionally has a bad name.
 
 TypeTuple is great because it enables "compile-time foreach" 
 (unfortunately, implicitly):
 
      foreach (m; __traits(allMembers, A)) {
          // This body is repeated for each member at compile time
      }
Is there a way I can build an ENUM from within the FOREACH? What I want to achive is, that I would like to use: final switch (myEnum) ... and have myEnum build at compiletime. With this the compiler would give an error whenever I have forgotten to update my code to handle a new case.
  > 2. I'm wondering why members1 doesn't has a type at all.
 
 Because it is a TypeTuple of values of various types and even types themselves.
But shouldn't be the type than be "TypeTuple"? -- Robert M. Münch http://www.saphirion.com smarter | better | faster
May 15 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 05/15/2015 09:45 AM, Robert M. Münch wrote:

 On 2015-05-04 18:22:17 +0000, Ali Çehreli said:
 TypeTuple is great because it enables "compile-time foreach"
 (unfortunately, implicitly):

      foreach (m; __traits(allMembers, A)) {
          // This body is repeated for each member at compile time
      }
Is there a way I can build an ENUM from within the FOREACH? What I want to achive is, that I would like to use: final switch (myEnum) ...
Sorry, I don't understand your question. :( Do you want to define an enum at compile time? Then you can use mixins. Do you want to build an enum value inside the foreach? Yes, it is possible as well.
 and have myEnum build at compiletime. With this the compiler would give
 an error whenever I have forgotten to update my code to handle a new 
case. I need a code example. Sorry. :(
  > 2. I'm wondering why members1 doesn't has a type at all.

 Because it is a TypeTuple of values of various types and even types
 themselves.
But shouldn't be the type than be "TypeTuple"?
A TypeTuple can contain types and values, which can be checked at compile time: import std.stdio; import std.typetuple; // A function template void foo(T...)() { foreach (i, a; T) { static if (is (a)) { writefln("Argument %s is a type: %s", i, a.stringof); } else { writefln("Argument %s is not : %s", i, a.stringof); } } } // Another one: void bar(T, size_t N)() { T[N] arr; } void main() { alias args = TypeTuple!(int, 42); foo!args(); bar!args(); } Ali
May 15 2015
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2015-05-15 17:26:50 +0000, Ali Çehreli said:

 On 05/15/2015 09:45 AM, Robert M. Münch wrote:
 
  > Is there a way I can build an ENUM from within the FOREACH? What I want
  > to achive is, that I would like to use:
  >
  > final switch (myEnum) ...
 
 Sorry, I don't understand your question. :(
 
 Do you want to define an enum at compile time? Then you can use mixins.
 
 Do you want to build an enum value inside the foreach? Yes, it is 
 possible as well.
Hi, yes to both. I have solved it now like this: enum A {a, b, c}; enum members1 = __traits(allMembers, A); // returns TypeTuple // function that generates a string which is used as a mixin at compile time // result string must conform to syntax as it was hand-written code string generateEnum(T...)(string type){ string code = "enum " ~ type ~ " {"; // this is a static foreach (compile time) foreach(m; T){ debug pragma(msg, m ~ ","); // check what code we get at compile time code ~= m ~ ","; } return(code ~ "}"); } int main(){ A switch_var_a; final switch(switch_var_a){ case A.a: case A.b: case A.c: } mixin(generateEnums!members1("B")); B switch_var_b; final switch(switch_var_b){ case B.a: // case B.b: // if commeted will cause a compiler error case B.c: } return(0); } So, the solution was to use a "string mixin". IMO it's a very powerful pattern to build an ENUM at compile time that can be used with a FINAL SWITCH. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
May 18 2015
prev sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Mon, 04 May 2015 20:07:27 +0200, Robert M. M=C3=BCnch wrote:

=20
 Gives this:
=20
 (string, string, string)
 playground.d(9): Error: no type for typeid(members1)
 playground.d(9):        while evaluating pragma(msg, typeid(members1))
`typeid` is runtime thing, you can't use it in compile-time. besides, as Ali said, `allMembers` returns tuple, which is very special=20 thing that exists only in compile-time.
 1. So the (string, string, string) output is based on the expression of
 members1? Meaning, the right-hand-side.
that's three field names for `enum A`. and field name is a string.
 2. I'm wondering why members1 doesn't has a type at all. So, we don't
 have a compile-time boolean?
it's a tuple. actually, `(string, string, string)` is a special type.
 3. For members2, where auto is used, I would expect that the return type
 of "allMembers" is used.
it is. ;-)
 Which would be a tuple. But again, no type for
 members2. Which I find quite strange.
as i said, `typeid` is runtime feature, so you can't print it with pragma.=20 and tuples aren't exist in runtime, it's compile-time only. i think you are even more confused now. ;-) sorry.=
May 04 2015
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2015-05-04 22:22:51 +0000, ketmar said:

 as i said, `typeid` is runtime feature, so you can't print it with pragma.
 and tuples aren't exist in runtime, it's compile-time only.
 
 
 i think you are even more confused now. ;-) sorry.
No, that makes it much more clearer for me. The compiler should print it: "No type. typeid() is run-time only." And everything would be clear. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
May 05 2015