Add TaggedUnion.toHash.

Allows using TaggedUnions as associative array keys.
This commit is contained in:
Sönke Ludwig 2020-08-27 15:52:59 +02:00
parent 2875f53c1d
commit bde8e6eeff
2 changed files with 63 additions and 0 deletions

View file

@ -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);
}

View file

@ -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]; } ();