www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Phobos' std.conv.to-conversion from enum to string doesn't scale

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
I've discovered the annoying fact that std.conv.to doesn't scale 
for enum to string conversion when the enum has hundreds of 
members. This because of a call to `NoDuplicates` which has (at 
least) O(n*log(n) time and space complexity.

So I've come up with


/** Faster implementation of `std.conv.to`.
  */
string toString(T)(T value)  safe pure nothrow  nogc
if (is(T == enum))
{
     final switch (value)
     {
         static foreach (member; __traits(allMembers, T))
         {
         case __traits(getMember, T, member):
             return member;
         }
     }
}

///
 safe pure nothrow  nogc unittest
{
     enum E { unknown, x, y, z, }
     assert(E.x.toString == "x");
     assert(E.y.toString == "y");
     assert(E.z.toString == "z");
}


The question know is: How do I make this support enums with 
enumerator aliases without needing to call `NoDuplicates`?

For instance, this should work

///
 safe pure nothrow  nogc unittest
{
     enum E { unknown, x, y, z, z_ = z, }
     assert(E.x.toString == "x");
     assert(E.y.toString == "y");
     assert(E.z.toString == "z");
     assert(E.z_.toString == "z");
}
Jun 21 2018
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/21/18 6:46 PM, Per Nordlöw wrote:
 I've discovered the annoying fact that std.conv.to doesn't scale for 
 enum to string conversion when the enum has hundreds of members. This 
 because of a call to `NoDuplicates` which has (at least) O(n*log(n) time 
 and space complexity.
 
 So I've come up with
 
 
 /** Faster implementation of `std.conv.to`.
   */
 string toString(T)(T value)  safe pure nothrow  nogc
 if (is(T == enum))
 {
      final switch (value)
      {
          static foreach (member; __traits(allMembers, T))
          {
          case __traits(getMember, T, member):
              return member;
          }
      }
 }
 
 ///
  safe pure nothrow  nogc unittest
 {
      enum E { unknown, x, y, z, }
      assert(E.x.toString == "x");
      assert(E.y.toString == "y");
      assert(E.z.toString == "z");
 }
 
 
 The question know is: How do I make this support enums with enumerator 
 aliases without needing to call `NoDuplicates`?
You can't. That's why it's in there. And I can't think of a better algorithm than O(nlgn). That's what it would cost to sort anyway. The sucky thing is, the compiler is *already* doing a sort on the items in the switch, and *already* doing the duplicate check. It would be cool to be able to leverage this mechanism to avoid the library solution, but I don't know how we can do that, as the semantics for switch are well defined, and there's no other way to hook this builtin functionality. One thing that may be worthwhile is checking to see if a CTFE solution is better than a template solution. In other words: string[] dedup(string[] identifiers) { ... } enum items = dedup(__traits(allMembers, T)); static foreach(member; items) ... Just avoiding all the template symbol generation may make it worth it, even if the dedup function is O(n^2) complexity. -Steve
Jun 21 2018
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer 
wrote:
 On 6/21/18 6:46 PM, Per Nordlöw wrote:
 I've discovered the annoying fact that std.conv.to doesn't 
 scale for enum to string conversion when the enum has hundreds 
 of members. This because of a call to `NoDuplicates` which has 
 (at least) O(n*log(n) time and space complexity.
 
 So I've come up with
 
 
 /** Faster implementation of `std.conv.to`.
   */
 string toString(T)(T value)  safe pure nothrow  nogc
 if (is(T == enum))
 {
      final switch (value)
      {
          static foreach (member; __traits(allMembers, T))
          {
          case __traits(getMember, T, member):
              return member;
          }
      }
 }
 
 ///
  safe pure nothrow  nogc unittest
 {
      enum E { unknown, x, y, z, }
      assert(E.x.toString == "x");
      assert(E.y.toString == "y");
      assert(E.z.toString == "z");
 }
 
 
 The question know is: How do I make this support enums with 
 enumerator aliases without needing to call `NoDuplicates`?
You can't. That's why it's in there. And I can't think of a better algorithm than O(nlgn). That's what it would cost to sort anyway. The sucky thing is, the compiler is *already* doing a sort on the items in the switch, and *already* doing the duplicate check. It would be cool to be able to leverage this mechanism to avoid the library solution, but I don't know how we can do that, as the semantics for switch are well defined, and there's no other way to hook this builtin functionality. One thing that may be worthwhile is checking to see if a CTFE solution is better than a template solution. In other words: string[] dedup(string[] identifiers) { ... } enum items = dedup(__traits(allMembers, T)); static foreach(member; items) ... Just avoiding all the template symbol generation may make it worth it, even if the dedup function is O(n^2) complexity. -Steve
I do have my own ctfe utils for this {https://forum.dlang.org/post/bfnwstkafhfgihavtzsz forum.dlang.org}, however without dedup (because I'd rather be alerted when it happens) what you have to do is to sort the enum values. (I'd suggest a non-recursive mergesort.) However if there are no duplicates there's no need to deduplicate, the reason it is deduplicated it because the switch_statement forbids duplicates. I may add that this performs very well with newCTFE I don't know about the old engine, but I'd assume even there it's faster.
Jun 22 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/22/18 1:41 PM, Stefan Koch wrote:
 On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer wrote:
 On 6/21/18 6:46 PM, Per Nordlöw wrote:
 I've discovered the annoying fact that std.conv.to doesn't scale for 
 enum to string conversion when the enum has hundreds of members. This 
 because of a call to `NoDuplicates` which has (at least) O(n*log(n) 
 time and space complexity.

 So I've come up with


 /** Faster implementation of `std.conv.to`.
   */
 string toString(T)(T value)  safe pure nothrow  nogc
 if (is(T == enum))
 {
      final switch (value)
      {
          static foreach (member; __traits(allMembers, T))
          {
          case __traits(getMember, T, member):
              return member;
          }
      }
 }

 ///
  safe pure nothrow  nogc unittest
 {
      enum E { unknown, x, y, z, }
      assert(E.x.toString == "x");
      assert(E.y.toString == "y");
      assert(E.z.toString == "z");
 }


 The question know is: How do I make this support enums with 
 enumerator aliases without needing to call `NoDuplicates`?
You can't. That's why it's in there. And I can't think of a better algorithm than O(nlgn). That's what it would cost to sort anyway. The sucky thing is, the compiler is *already* doing a sort on the items in the switch, and *already* doing the duplicate check. It would be cool to be able to leverage this mechanism to avoid the library solution, but I don't know how we can do that, as the semantics for switch are well defined, and there's no other way to hook this builtin functionality. One thing that may be worthwhile is checking to see if a CTFE solution is better than a template solution. In other words: string[] dedup(string[] identifiers) { ... } enum items = dedup(__traits(allMembers, T)); static foreach(member; items) ... Just avoiding all the template symbol generation may make it worth it, even if the dedup function is O(n^2) complexity.
I do have my own ctfe utils for this {https://forum.dlang.org/post/bfnwstkafhfgihavtzsz forum.dlang.org}, however without dedup (because I'd rather be alerted when it happens) what you have to do is to sort the enum values. (I'd suggest a non-recursive mergesort.)
How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
 However if there are no duplicates there's no need to deduplicate, the 
 reason it is deduplicated it because the switch_statement forbids 
 duplicates.
If you can sort, deduplicating is as easy as checking if the value is the same as the previous. So really, we just need a good compile-time sort for strings.
 I may add that this performs very well with newCTFE I don't know about 
 the old engine, but I'd assume even there it's faster.
I think your algorithm is roughly the same as Nordlow's. Basically you are using the compiler to sort the array (as it does anyway for a switch statement). -Steve
Jun 22 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/22/18 2:15 PM, Steven Schveighoffer wrote:
 On 6/22/18 1:41 PM, Stefan Koch wrote:
 (I'd suggest a non-recursive mergesort.)
How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
This kind of sounded like a fun experiment, so I tried it: https://run.dlang.io/gist/7c23567e0fa2e3b663d0b0695d3c257e No idea of the performance or capability given hundreds of enum members, but something new to try. -Steve
Jun 22 2018
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 22 June 2018 at 20:23:51 UTC, Steven Schveighoffer 
wrote:
 On 6/22/18 2:15 PM, Steven Schveighoffer wrote:
 On 6/22/18 1:41 PM, Stefan Koch wrote:
 (I'd suggest a non-recursive mergesort.)
How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
This kind of sounded like a fun experiment, so I tried it: https://run.dlang.io/gist/7c23567e0fa2e3b663d0b0695d3c257e No idea of the performance or capability given hundreds of enum members, but something new to try. -Steve
run.dlang.io is down for me. maybe you can provide a gh link?
Jun 22 2018
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/22/18 4:56 PM, Stefan Koch wrote:
 On Friday, 22 June 2018 at 20:23:51 UTC, Steven Schveighoffer wrote:
 On 6/22/18 2:15 PM, Steven Schveighoffer wrote:
 On 6/22/18 1:41 PM, Stefan Koch wrote:
 (I'd suggest a non-recursive mergesort.)
How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
This kind of sounded like a fun experiment, so I tried it: https://run.dlang.io/gist/7c23567e0fa2e3b663d0b0695d3c257e No idea of the performance or capability given hundreds of enum members, but something new to try.
run.dlang.io is down for me. maybe you can provide a gh link?
https://gist.github.com/run-dlang/d023233104927e61ba69e79c03d266e4 -Steve
Jun 22 2018
prev sibling next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Friday, 22 June 2018 at 20:56:58 UTC, Stefan Koch wrote:
 How will that perform in CTFE? I'm concerned about swapping 
 values making it allocate new arrays all over the place.
What about creating a bit-array with the size of the enumerator count and use that to detect duplicates? How well would a mutating bitarray play in CTFE?
Jun 24 2018
prev sibling parent Seb <seb wilzba.ch> writes:
On Friday, 22 June 2018 at 20:56:58 UTC, Stefan Koch wrote:
 On Friday, 22 June 2018 at 20:23:51 UTC, Steven Schveighoffer 
 wrote:
 On 6/22/18 2:15 PM, Steven Schveighoffer wrote:
 On 6/22/18 1:41 PM, Stefan Koch wrote:
 (I'd suggest a non-recursive mergesort.)
How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
This kind of sounded like a fun experiment, so I tried it: https://run.dlang.io/gist/7c23567e0fa2e3b663d0b0695d3c257e No idea of the performance or capability given hundreds of enum members, but something new to try. -Steve
run.dlang.io is down for me. maybe you can provide a gh link?
Huh? There was never any downtime. Do you still have troubles? (if so, it would be good to figure out what's happening)
Jun 24 2018
prev sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer 
wrote:
 The sucky thing is, the compiler is *already* doing a sort on 
 the items in the switch, and *already* doing the duplicate 
 check. It would be cool to be able to leverage this mechanism 
 to avoid the library solution, but I don't know how we can do 
 that, as the semantics for switch are well defined, and there's 
 no other way to hook this builtin functionality.
I would like to see a new trait named, say, `primaryMembers` or `nonAliasMembers` that returns exactly what the switch needs. I believe this is motivated by the fact that this is a serious issue; Without the user notices, the use of enums combined with .to!string or io sucks up more and more RAM in the compiler as new members are added. If enough (hundreds) of members are added you can get out of RAM, which is what happened to me. What do you think about that idea? We should plot compilation time and memory usage with enumerator count so we can reason about the severity of this issue.
Jun 24 2018
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/24/18 11:46 AM, Per Nordlöw wrote:
 On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer wrote:
 The sucky thing is, the compiler is *already* doing a sort on the 
 items in the switch, and *already* doing the duplicate check. It would 
 be cool to be able to leverage this mechanism to avoid the library 
 solution, but I don't know how we can do that, as the semantics for 
 switch are well defined, and there's no other way to hook this builtin 
 functionality.
I would like to see a new trait named, say, `primaryMembers` or `nonAliasMembers` that returns exactly what the switch needs. I believe this is motivated by the fact that this is a serious issue; Without the user notices, the use of enums combined with ..to!string or io sucks up more and more RAM in the compiler as new members are added. If enough (hundreds) of members are added you can get out of RAM, which is what happened to me. What do you think about that idea? We should plot compilation time and memory usage with enumerator count so we can reason about the severity of this issue.
Hm... just had another thought. Many times, the enum has no repeating members, so your mechanism just works. So just use that if it works! static if(__traits(compiles, fastEnumToString(val))) return fastEnumToString(val); else return slowEnumToString(val); // checks for duplicates Should eliminate the issues, because it's not going to compile the slow version if the fast version can work. -Steve
Jun 24 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 17:23:54 UTC, Steven Schveighoffer 
wrote:
 static if(__traits(compiles, fastEnumToString(val)))
   return fastEnumToString(val);
 else
   return slowEnumToString(val); // checks for duplicates

 Should eliminate the issues, because it's not going to compile 
 the slow version if the fast version can work.

 -Steve
Yes, I thought about that too, but the problem is that std.conv.to is used in std.stdio and I don't want to remember to always to do writeln("Some text:", x.to!string); instead of writeln("Some text:", x); for some enum instance `x`. I'm gonna hack up another solution struct Enum(E) if (is(E == enum)) { property string toString() safe pure nothrow nogc { // fast implementation } E _enum; alias _enum this; } Further, it just struck me that we can generalize my fast solution to include enumerations with enumerator aliases that are defined directly after its original enumerator by checking with a `static if` if the current enumerator value equals the previous then we skip it. I'm gonna post the solution here after some hacking.
Jun 24 2018
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 21:47:14 UTC, Per Nordlöw wrote:
 Yes, I thought about that too, but the problem is that 
 std.conv.to is used in std.stdio and I don't want to remember 
 to always to do

     writeln("Some text:", x.to!string);
Or rather writeln("Some text:", x.toString);
Jun 24 2018
prev sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 21:47:14 UTC, Per Nordlöw wrote:
 Further, it just struck me that we can generalize my fast 
 solution to include enumerations with enumerator aliases that 
 are defined directly after its original enumerator by checking 
 with  a `static if` if the current enumerator value equals the 
 previous then we skip it. I'm gonna post the solution here 
 after some hacking.
Solution: safe: /** Enumeration wrapper that uses optimized conversion to string (via `toString` * member). */ struct Enum(E) if (is(E == enum)) { property string toString() safe pure nothrow nogc { final switch (_enum) { static foreach (index, member; __traits(allMembers, E)) { static if (index == 0 || (__traits(getMember, E, __traits(allMembers, E)[index - 1]) != __traits(getMember, E, member))) { case __traits(getMember, E, member): return member; } } } } E _enum; // the wrapped enum alias _enum this; } safe pure unittest { import std.conv : to; enum X { a, b, _b = b // enumerator alias } alias EnumX = Enum!X; assert(EnumX(X.a).to!string == "a"); assert(EnumX(X.b).to!string == "b"); assert(EnumX(X._b).to!string == "b"); }
Jun 24 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/24/18 7:13 PM, Per Nordlöw wrote:
 On Sunday, 24 June 2018 at 21:47:14 UTC, Per Nordlöw wrote:
 Further, it just struck me that we can generalize my fast solution to 
 include enumerations with enumerator aliases that are defined directly 
 after its original enumerator by checking with  a `static if` if the 
 current enumerator value equals the previous then we skip it. I'm 
 gonna post the solution here after some hacking.
Solution: safe: /** Enumeration wrapper that uses optimized conversion to string (via `toString`  * member).  */ struct Enum(E) if (is(E == enum)) {     property string toString() safe pure nothrow nogc     {         final switch (_enum)         {             static foreach (index, member; __traits(allMembers, E))             {                 static if (index == 0 ||                            (__traits(getMember, E, __traits(allMembers, E)[index - 1]) !=                             __traits(getMember, E, member)))                 {                 case __traits(getMember, E, member):                     return member;                 }             }         }     }     E _enum;                    // the wrapped enum     alias _enum this; } safe pure unittest {     import std.conv : to;     enum X { a,              b,              _b = b             // enumerator alias     }     alias EnumX = Enum!X;     assert(EnumX(X.a).to!string == "a");     assert(EnumX(X.b).to!string == "b");     assert(EnumX(X._b).to!string == "b"); }
Great! That should cover quite a few cases. Now, about this:
 Yes, I thought about that too, but the problem is that std.conv.to is 
 used in std.stdio and I don't want to remember to always to do
 
      writeln("Some text:", x.toString);
 
 instead of
 
      writeln("Some text:", x);
You misunderstand -- I mean fix std.conv.to so it does what you want. I don't want your code to be the only one that benefits, I want all code to benefit ;) It's shouldn't be that difficult of a PR. Just separate out the part that takes an AliasSeq and makes the switch, and try without and with NoDuplicates (whichever works first wins). -Steve
Jun 24 2018
parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 23:21:44 UTC, Steven Schveighoffer 
wrote:
       property string toString()  safe pure nothrow  nogc
      {
          final switch (_enum)
          {
              static foreach (index, member; 
 __traits(allMembers, E))
              {
                  static if (index == 0 ||
                             (__traits(getMember, E, 
 __traits(allMembers, E)[index - 1]) !=
                              __traits(getMember, E,
member)))
                  {
                  case __traits(getMember, E, member):
                      return member;
                  }
              }
          }
      }
      E _enum;                    // the wrapped enum
      alias _enum this;
 }
Provided that __traits(allMembers, E) is a cheap operation as it's called once for every enumerator. I could get it out of the loop; if I do as property string toString() safe pure nothrow nogc { final switch (_enum) { enum members = __traits(allMembers, E); static foreach (index, member; __traits(allMembers, E)) { static if (index == 0 || (__traits(getMember, E, members[index - 1]) != __traits(getMember, E, member))) { case __traits(getMember, E, member): return member; } } } } the compiler complains as enum_ex.d(19,29): Error: expression expected as second argument of __traits `getMember` Is __traits(allMembers, ...) cached by the compiler?
Jun 24 2018
parent reply Timoses <timosesu gmail.com> writes:
On Sunday, 24 June 2018 at 23:34:49 UTC, Per Nordlöw wrote:
 Provided that

     __traits(allMembers, E)

 is a cheap operation as it's called once for every enumerator. 
 I could get it out of the loop; if I do as

      property string toString()  safe pure nothrow  nogc
     {
         final switch (_enum)
         {
             enum members = __traits(allMembers, E);
enum members = [_traits(allMembers, E)]; seems to work
Jun 24 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 24, 2018 23:53:09 Timoses via Digitalmars-d wrote:
 On Sunday, 24 June 2018 at 23:34:49 UTC, Per Nordlöw wrote:
 Provided that

     __traits(allMembers, E)

 is a cheap operation as it's called once for every enumerator.
 I could get it out of the loop; if I do as

      property string toString()  safe pure nothrow  nogc
     {

         final switch (_enum)
         {

             enum members = __traits(allMembers, E);
enum members = [_traits(allMembers, E)]; seems to work
Or if you want it to stay an AliasSeq, then just use Alias or AliasSeq on it. e.g. alias members = AliasSeq!(__traits(allMembers, E)); Given that the result of __traits in this case is an AliasSeq, I have no idea why the gramar doesn't allow you to just use the result of __traits directly as an AliasSeq with something like alias members = __traits(allMembers, E); but it doesn't. So, you have to wrap it, dumb as that may be. There's a bug report about it in bugzilla somewhere. - Jonathan M Davis
Jun 24 2018
next sibling parent reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Monday, 25 June 2018 at 00:35:40 UTC, Jonathan M Davis wrote:
 Or if you want it to stay an AliasSeq, then just use Alias or 
 AliasSeq on it. e.g.

 alias members = AliasSeq!(__traits(allMembers, E));
Thanks! Should we prefer this over enum members = [__traits(allMembers, E)]; ?
Jun 25 2018
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Monday, 25 June 2018 at 07:43:53 UTC, Per Nordlöw wrote:
 On Monday, 25 June 2018 at 00:35:40 UTC, Jonathan M Davis wrote:
 Or if you want it to stay an AliasSeq, then just use Alias or 
 AliasSeq on it. e.g.

 alias members = AliasSeq!(__traits(allMembers, E));
Thanks! Should we prefer this over enum members = [__traits(allMembers, E)]; ?
I tested on a really big enum: alias members = AliasSeq!(__traits(allMembers, E)); is faster. :)
Jun 25 2018
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, June 25, 2018 07:43:53 Per Nordlöw via Digitalmars-d wrote:
 On Monday, 25 June 2018 at 00:35:40 UTC, Jonathan M Davis wrote:
 Or if you want it to stay an AliasSeq, then just use Alias or
 AliasSeq on it. e.g.

 alias members = AliasSeq!(__traits(allMembers, E));
Thanks! Should we prefer this over enum members = [__traits(allMembers, E)];
Which is better depends on what you're doing. alias members = AliasSeq!(_traits(allMembers, E)); gives you an AliasSeq, whereas enum members = [__traits(allMembers, E)]; gives you a dynamic array. The AliasSeq means needing to use templates to operate on it, whereas using the dynamic array means using CTFE to operate on it. If you're just going to use a static foreach on it, I wouldn't expect it to matter much which you use, but ultimately, you'll have to see what works best with your exact use case. In the long run, once newCTFE is complete, I suspect that using CTFE where possible instead of templates is going to be better, but I think that it tends to be less of a clear win with how inefficient CTFE currently is. But again, it depends on what exactly the code is doing. - Jonathan M Davis
Jun 25 2018
prev sibling parent Timoses <timosesu gmail.com> writes:
On Monday, 25 June 2018 at 00:35:40 UTC, Jonathan M Davis wrote:
 On Sunday, June 24, 2018 23:53:09 Timoses via Digitalmars-d 
 wrote:
 On Sunday, 24 June 2018 at 23:34:49 UTC, Per Nordlöw wrote:
 Provided that

     __traits(allMembers, E)

 is a cheap operation as it's called once for every 
 enumerator. I could get it out of the loop; if I do as

      property string toString()  safe pure nothrow  nogc
     {

         final switch (_enum)
         {

             enum members = __traits(allMembers, E);
enum members = [_traits(allMembers, E)]; seems to work
Or if you want it to stay an AliasSeq, then just use Alias or AliasSeq on it. e.g. alias members = AliasSeq!(__traits(allMembers, E)); Given that the result of __traits in this case is an AliasSeq, I have no idea why the gramar doesn't allow you to just use the result of __traits directly as an AliasSeq with something like alias members = __traits(allMembers, E); but it doesn't. So, you have to wrap it, dumb as that may be. There's a bug report about it in bugzilla somewhere.
https://issues.dlang.org/show_bug.cgi?id=16390 I guess? First search result : D
Jun 25 2018
prev sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 23:53:09 UTC, Timoses wrote:
 enum members = [_traits(allMembers, E)];

 seems to work
Great! Now becomes: safe: /** Enumeration wrapper that uses optimized conversion to string (via `toString` * member). */ struct Enum(E) if (is(E == enum)) { property string toString() safe pure nothrow nogc { enum members = [__traits(allMembers, E)]; final switch (_enum) { static foreach (index, member; members) { static if (index == 0 || (__traits(getMember, E, members[index - 1]) != __traits(getMember, E, member))) { case __traits(getMember, E, member): return member; } } } } E _enum; // the wrapped enum alias _enum this; }
Jun 25 2018
prev sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 15:46:58 UTC, Per Nordlöw wrote:
 I would like to see a new trait named, say, `primaryMembers` or 
 `nonAliasMembers` that returns exactly what the switch needs.
Alternatively, we could define a new `__traits(isAlias, enumeratorSymbol)` that evaluates to true for enumerator to aliases and use that to static-if-filter inside the static foreach loop. Comments on that!
Jun 24 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, June 21, 2018 22:46:23 Per Nordlöw via Digitalmars-d wrote:
 I've discovered the annoying fact that std.conv.to doesn't scale
 for enum to string conversion when the enum has hundreds of
 members. This because of a call to `NoDuplicates` which has (at
 least) O(n*log(n) time and space complexity.
I'm certainly not going to argue against trying to make std.conv.to faster, but what on earth are you doing that you have an enum with hundreds of members? I would have thought that an enum that had anywhere near fifty members was enormous, let alone hundreds. I'm not sure that I've ever dealt with an enum that had more than maybe a couple dozen members. - Jonathan M Davis
Jun 24 2018
parent =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Sunday, 24 June 2018 at 22:33:09 UTC, Jonathan M Davis wrote:
 On Thursday, June 21, 2018 22:46:23 Per Nordlöw via 
 Digitalmars-d wrote:
 I've discovered the annoying fact that std.conv.to doesn't 
 scale
 for enum to string conversion when the enum has hundreds of
 members. This because of a call to `NoDuplicates` which has (at
 least) O(n*log(n) time and space complexity.
I'm certainly not going to argue against trying to make std.conv.to faster, but what on earth are you doing that you have an enum with hundreds of members? I would have thought that an enum that had anywhere near fifty members was enormous, let alone hundreds. I'm not sure that I've ever dealt with an enum that had more than maybe a couple dozen members. - Jonathan M Davis
A knowledge database. The relation kind enum statically mirrors all the relations in a set of very large ontologies. I might decide to make it dynamically defined instead...
Jun 24 2018