digitalmars.D - Phobos' std.conv.to-conversion from enum to string doesn't scale
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (39/39) Jun 21 2018 I've discovered the annoying fact that std.conv.to doesn't scale
- Steven Schveighoffer (16/51) Jun 21 2018 You can't. That's why it's in there. And I can't think of a better
- Stefan Koch (10/63) Jun 22 2018 I do have my own ctfe utils for this
- Steven Schveighoffer (10/82) Jun 22 2018 How will that perform in CTFE? I'm concerned about swapping values
- Steven Schveighoffer (6/11) Jun 22 2018 This kind of sounded like a fun experiment, so I tried it:
- Stefan Koch (4/15) Jun 22 2018 run.dlang.io is down for me.
- Steven Schveighoffer (3/22) Jun 22 2018 https://gist.github.com/run-dlang/d023233104927e61ba69e79c03d266e4
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (4/6) Jun 24 2018 What about creating a bit-array with the size of the enumerator
- Seb (4/23) Jun 24 2018 Huh? There was never any downtime.
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (12/18) Jun 24 2018 I would like to see a new trait named, say, `primaryMembers` or
- Steven Schveighoffer (11/30) Jun 24 2018 Hm... just had another thought.
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (26/33) Jun 24 2018 Yes, I thought about that too, but the problem is that
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (3/7) Jun 24 2018 Or rather
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (43/49) Jun 24 2018 Solution:
- Steven Schveighoffer (10/70) Jun 24 2018 Great! That should cover quite a few cases.
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (31/52) Jun 24 2018 Provided that
- Timoses (3/12) Jun 24 2018 enum members = [_traits(allMembers, E)];
- Jonathan M Davis (11/28) Jun 24 2018 Or if you want it to stay an AliasSeq, then just use Alias or AliasSeq o...
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (4/7) Jun 25 2018 Thanks! Should we prefer this over
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (4/12) Jun 25 2018 I tested on a really big enum:
- Jonathan M Davis (15/22) Jun 25 2018 Which is better depends on what you're doing.
- Timoses (3/33) Jun 25 2018 https://issues.dlang.org/show_bug.cgi?id=16390
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (32/34) Jun 25 2018 Great!
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (5/7) Jun 24 2018 Alternatively, we could define a new `__traits(isAlias,
- Jonathan M Davis (7/11) Jun 24 2018 I'm certainly not going to argue against trying to make std.conv.to fast...
- =?UTF-8?B?Tm9yZGzDtnc=?= (4/18) Jun 24 2018 A knowledge database. The relation kind enum statically mirrors
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
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
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 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.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 22 2018
On 6/22/18 1:41 PM, Stefan Koch wrote:On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer wrote:How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.On 6/21/18 6:46 PM, Per Nordlöw wrote: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.)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.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
On 6/22/18 2:15 PM, Steven Schveighoffer wrote:On 6/22/18 1:41 PM, Stefan Koch wrote: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(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.
Jun 22 2018
On Friday, 22 June 2018 at 20:23:51 UTC, Steven Schveighoffer wrote:On 6/22/18 2:15 PM, Steven Schveighoffer wrote:run.dlang.io is down for me. maybe you can provide a gh link?On 6/22/18 1:41 PM, Stefan Koch wrote: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(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.
Jun 22 2018
On 6/22/18 4:56 PM, Stefan Koch wrote:On Friday, 22 June 2018 at 20:23:51 UTC, Steven Schveighoffer wrote:https://gist.github.com/run-dlang/d023233104927e61ba69e79c03d266e4 -SteveOn 6/22/18 2:15 PM, Steven Schveighoffer wrote:run.dlang.io is down for me. maybe you can provide a gh link?On 6/22/18 1:41 PM, Stefan Koch wrote: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.(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.
Jun 22 2018
On Friday, 22 June 2018 at 20:56:58 UTC, Stefan Koch wrote: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?How will that perform in CTFE? I'm concerned about swapping values making it allocate new arrays all over the place.
Jun 24 2018
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:Huh? There was never any downtime. Do you still have troubles? (if so, it would be good to figure out what's happening)On 6/22/18 2:15 PM, Steven Schveighoffer wrote:run.dlang.io is down for me. maybe you can provide a gh link?On 6/22/18 1:41 PM, Stefan Koch wrote: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(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.
Jun 24 2018
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
On 6/24/18 11:46 AM, Per Nordlöw wrote:On Friday, 22 June 2018 at 00:50:05 UTC, Steven Schveighoffer wrote: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. -SteveThe 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
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. -SteveYes, 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
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
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
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:Great! That should cover quite a few cases. Now, about 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.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"); }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
On Sunday, 24 June 2018 at 23:21:44 UTC, Steven Schveighoffer 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); 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?   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; }
Jun 24 2018
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
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: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 DavisProvided 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
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
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:I tested on a really big enum: alias members = AliasSeq!(__traits(allMembers, E)); is faster. :)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
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: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 DavisOr 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
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:https://issues.dlang.org/show_bug.cgi?id=16390 I guess? First search result : DOn Sunday, 24 June 2018 at 23:34:49 UTC, Per Nordlöw 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)); 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.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 25 2018
On Sunday, 24 June 2018 at 23:53:09 UTC, Timoses wrote:enum members = [_traits(allMembers, E)]; seems to workGreat! 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
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
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
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: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...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