From bde8e6eefff7ed45d0e1a7e21ef1486345d34b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 27 Aug 2020 15:52:59 +0200 Subject: [PATCH] Add TaggedUnion.toHash. Allows using TaggedUnions as associative array keys. --- source/taggedalgebraic/taggedalgebraic.d | 21 ++++++++++++ source/taggedalgebraic/taggedunion.d | 42 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/source/taggedalgebraic/taggedalgebraic.d b/source/taggedalgebraic/taggedalgebraic.d index bd932a9..d0e8f20 100644 --- a/source/taggedalgebraic/taggedalgebraic.d +++ b/source/taggedalgebraic/taggedalgebraic.d @@ -108,6 +108,13 @@ struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct) || is(U == enum) @property auto ref opDispatch(this TA, ARGS...)(auto ref ARGS args) if (hasOp!(TA, OpKind.field, name, ARGS) && !hasOp!(TA, OpKind.method, name, ARGS)) { return implementOp!(OpKind.field, name)(this, args); } } + static if (is(typeof(m_union.toHash()))) { + size_t toHash() + const @safe nothrow { + return m_union.toHash(); + } + } + /// Enables equality comparison with the stored value. auto ref opEquals(T, this TA)(auto ref T other) if (is(Unqual!T == TaggedAlgebraic) || hasOp!(TA, OpKind.binary, "==", T)) @@ -1390,3 +1397,17 @@ unittest { auto ta = TA(12); static assert(!is(typeof(ta.put(12)))); } + +@safe nothrow unittest { + struct TU { int i; string s; } + alias TA = TaggedAlgebraic!TU; + + static assert(is(typeof(TA.init.toHash()) == size_t)); + + int[TA] aa; + aa[TA(1)] = 1; + aa[TA("foo")] = 2; + + assert(aa[TA(1)] == 1); + assert(aa[TA("foo")] == 2); +} diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index 6351783..667e9fa 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -202,6 +202,26 @@ align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion assert(false); // never reached } + static if (allSatisfy!(isHashable, FieldTypes)) + { + /// Enables using a tagged union value as an associative array key. + size_t toHash() + const @safe nothrow { + size_t ret; + final switch (m_kind) { + foreach (i, tname; fieldNames) { + alias T = FieldTypes[i]; + case __traits(getMember, Kind, tname): + static if (!isUnitType!T) { + ret = hashOf(trustedGet!T); + } + break; + } + } + return ret ^ (m_kind * 0xBA7A57E3); + } + } + /// The type ID of the currently stored value. @property Kind kind() const { return m_kind; } @@ -547,6 +567,20 @@ unittest { // support trailing underscores properly assert(val.intValue == 20); } +@safe nothrow unittest { + static struct S { int i; string s; } + alias TU = TaggedUnion!S; + + static assert(is(typeof(TU.init.toHash()) == size_t)); + + int[TU] aa; + aa[TU(1)] = 1; + aa[TU("foo")] = 2; + + assert(aa[TU(1)] == 1); + assert(aa[TU("foo")] == 2); +} + @property auto forceNothrowPostblit() { @@ -733,6 +767,14 @@ unittest { } } +private template isHashable(T) +{ + static if (isUnitType!T) enum isHashable = true; + else static if (__traits(compiles, (ref const(T) val) @safe nothrow => hashOf(val))) + enum isHashable = true; + else enum isHashable = false; +} + package void rawEmplace(T)(void[] dst, ref T src) { T[] tdst = () @trusted { return cast(T[])dst[0 .. T.sizeof]; } ();