1 module nxt.enum_ex; 2 3 @safe: 4 5 /** Enumeration wrapper that uses optimized conversion to string (via `toString` 6 * member). 7 * 8 * See_Also: https://forum.dlang.org/thread/ppndhxvzayedgpbjculm@forum.dlang.org?page=1 9 * 10 * TODO: Move logic to `std.conv.to`. 11 */ 12 struct Enum(E) 13 if (is(E == enum)) { 14 @property string toString() pure nothrow @safe @nogc => toStringFromEnumWithConsecutiveAliases(_enum); 15 E _enum; // the wrapped enum 16 alias _enum this; 17 } 18 19 /// 20 pure @safe unittest { 21 enum X { a, 22 b, 23 } 24 alias EnumX = Enum!X; 25 assert(EnumX(X.a).toString == "a"); 26 assert(EnumX(X.b).toString == "b"); 27 } 28 29 /** Fast and more generic implementation of `std.conv.to` for enumerations. 30 TODO: Handle non-adjacent enumerator aliases. 31 */ 32 string toStringFromEnumWithConsecutiveAliases(T)(const scope T value) pure nothrow @safe @nogc 33 if (is(T == enum)) { 34 alias members = __traits(allMembers, T); 35 final switch (value) { 36 static foreach (index, member; members) { 37 static if (index == 0 || 38 (__traits(getMember, T, members[index - 1]) != 39 __traits(getMember, T, member))) { 40 case __traits(getMember, T, member): 41 return member; 42 } 43 } 44 } 45 } 46 47 /// 48 pure nothrow @safe @nogc unittest { 49 enum E { unknown, x, y, z, z_ = z, } 50 assert(E.x.toStringFromEnumWithConsecutiveAliases == "x"); 51 assert(E.y.toStringFromEnumWithConsecutiveAliases == "y"); 52 assert(E.z.toStringFromEnumWithConsecutiveAliases == "z"); 53 assert(E.z_.toStringFromEnumWithConsecutiveAliases == "z"); 54 } 55 56 /** Faster implementation of `std.conv.to` for enumerations with no aliases. 57 Will error if aliases are present. 58 */ 59 string toStringFromEnumWithNoAliases(T)(const scope T value) pure nothrow @safe @nogc 60 if (is(T == enum)) /+ TODO: check for no aliases +/ { 61 final switch (value) { 62 static foreach (member; __traits(allMembers, T)) { 63 case __traits(getMember, T, member): 64 return member; 65 } 66 } 67 } 68 69 /// 70 pure nothrow @safe @nogc unittest { 71 enum E { unknown, x, y, z, } 72 assert(E.x.toStringFromEnumWithNoAliases == "x"); 73 assert(E.y.toStringFromEnumWithNoAliases == "y"); 74 assert(E.z.toStringFromEnumWithNoAliases == "z"); 75 } 76 77 /** Convert enumerator value `v` to `string`. 78 See_Also: http://forum.dlang.org/post/aqqhlbaepoimpopvouwv@forum.dlang.org 79 */ 80 string toStringFromEnumThatMimicsPhobos(T)(T v) if (is(T == enum)) { 81 switch (v) { 82 foreach (m; __traits(allMembers, T)) { 83 case mixin("T." ~ m) : return m; 84 } 85 default: { 86 char[] result = ("cast(" ~ T.stringof ~ ")").dup; 87 uint val = v; 88 89 enum headLength = T.stringof.length + "cast()".length; 90 const uint log10Val = (val < 10) ? 0 : (val < 100) ? 1 : (val < 1_000) ? 2 : 91 (val < 10_000) ? 3 : (val < 100_000) ? 4 : (val < 1_000_000) ? 5 : 92 (val < 10_000_000) ? 6 : (val < 100_000_000) ? 7 : (val < 1000_000_000) ? 8 : 9; 93 94 result.length += log10Val + 1; 95 96 foreach (uint i; 0 .. log10Val + 1) { 97 cast(char)result[headLength + log10Val - i] = cast(char) ('0' + (val % 10)); 98 val /= 10; 99 } 100 return () @trusted { return cast(string) result; }(); 101 } 102 } 103 } 104 105 pure nothrow @safe unittest { 106 enum ET { one, two } 107 // static assert(to!string(ET.one) == "one"); 108 static assert (toStringFromEnumThatMimicsPhobos(ET.one) == "one"); 109 assert (toStringFromEnumThatMimicsPhobos(ET.one) == "one"); 110 }