Merge pull request #25 from s-ludwig/tagged_union_improvements

Tagged union improvements
This commit is contained in:
Leonid Kramer 2019-02-23 19:27:39 +01:00 committed by GitHub
commit 995c0779a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -203,7 +203,20 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
} else {
mixin("static @property TaggedUnion "~name~"() { TaggedUnion tu; tu.set!(Kind."~name~"); return tu; }");
}
}
/** Checks whether the currently stored value has a given type.
*/
@property bool hasType(T)()
const {
static assert(staticIndexOf!(T, FieldTypes) >= 0, "Type "~T.stringof~ " not part of "~FieldTypes.stringof);
final switch (this.kind) {
static foreach (i, n; fieldNames) {
case __traits(getMember, Kind, n):
return is(FieldTypes[i] == T);
}
}
}
/** Accesses the contained value by reference.
@ -237,12 +250,13 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
See_Also: `set`, `opAssign`
*/
@property ref inout(T) value(T)() inout
if (staticIndexOf!(T, FieldTypes) >= 0)
{
static assert(staticIndexOf!(T, FieldTypes) >= 0, "Type "~T.stringof~ " not part of "~FieldTypes.stringof);
final switch (this.kind) {
static foreach (n; fieldNames) {
static foreach (i, n; fieldNames) {
case __traits(getMember, Kind, n):
static if (is(FieldTypes[__traits(getMember, Kind, n)] == T))
static if (is(FieldTypes[i] == T))
return trustedGet!T;
else assert(false, "Attempting to get type "~T.stringof
~ " from a TaggedUnion with type "
@ -364,6 +378,22 @@ unittest { // test for name clashes
assert(tu.stringValue == "foo");
}
unittest { // test woraround for Phobos issue 19696
struct T {
struct F { int num; }
alias Payload = TaggedUnion!F;
Payload payload;
alias payload this;
}
struct U {
T t;
}
alias TU = TaggedUnion!U;
static assert(is(TU.FieldTypes[0] == T));
}
/** Dispatches the value contained on a `TaggedUnion` to a set of visitors.
@ -383,6 +413,8 @@ template visit(VISITORS...)
auto visit(TU)(auto ref TU tu)
if (isInstanceOf!(TaggedUnion, TU))
{
alias val = validateHandlers!(TU, VISITORS);
final switch (tu.kind) {
static foreach (k; EnumMembers!(TU.Kind)) {
case k: {
@ -402,23 +434,27 @@ template visit(VISITORS...)
///
unittest {
union U {
int number;
string text;
static if (__VERSION__ >= 2081) {
import std.conv : to;
union U {
int number;
string text;
}
alias TU = TaggedUnion!U;
auto tu = TU.number(42);
tu.visit!(
(int n) { assert(n == 42); },
(string s) { assert(false); }
);
assert(tu.visit!((v) => to!int(v)) == 42);
tu.setText("43");
assert(tu.visit!((v) => to!int(v)) == 43);
}
alias TU = TaggedUnion!U;
auto tu = TU.number(42);
tu.visit!(
(int n) { assert(n == 42); },
(string s) { assert(false); }
);
assert(tu.visit!((v) => to!int(v)) == 42);
tu.setText("43");
assert(tu.visit!((v) => to!int(v)) == 43);
}
unittest {
@ -431,7 +467,6 @@ unittest {
//
static assert(is(typeof(u.visit!((int) {}, (float) {}, () {}))));
u.visit!((_) {}, () {});
static assert(is(typeof(u.visit!((_) {}, () {}))));
static assert(is(typeof(u.visit!((_) {}, (float) {}, () {}))));
static assert(is(typeof(u.visit!((float) {}, (_) {}, () {}))));
@ -449,14 +484,6 @@ unittest {
}
// workaround for "template to is not defined" error in the unit test above
// happens on DMD 2.080 and below
private U to(U, T)(T val) {
static import std.conv;
return std.conv.to!U(val);
}
/** The same as `visit`, except that failure to handle types is checked at runtime.
Instead of failing to compile, `tryVisit` will throw an `Exception` if none
@ -512,20 +539,23 @@ private template validateHandlers(TU, VISITORS...)
}
}
private template matchesType(alias fun, T)
{
static if (isSomeFunction!fun) {
alias Params = ParameterTypeTuple!fun;
static if (Params.length == 0 && is(T == void)) enum matchesType = true;
else static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true;
else enum matchesType = false;
} else static if (!is(T == void)) {
static if (isSomeFunction!(fun!T)) {
alias Parms = ParameterTypeTuple!fun;
static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true;
private template matchesType(alias fun) {
import std.traits : ParameterTypeTuple, isSomeFunction;
template matchesType(T) {
static if (isSomeFunction!fun) {
alias Params = ParameterTypeTuple!fun;
static if (Params.length == 0 && isUnitType!T) enum matchesType = true;
else static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true;
else enum matchesType = false;
} else static if (!isUnitType!T) {
static if (isSomeFunction!(fun!T)) {
alias Params = ParameterTypeTuple!(fun!T);
static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true;
else enum matchesType = false;
} else enum matchesType = false;
} else enum matchesType = false;
} else enum matchesType = false;
}
}
private template selectHandler(T, VISITORS...)
@ -599,7 +629,17 @@ template TypeOf(alias kind)
else alias FT = uda[0];
}
alias TypeOf = ReplaceType!(This, U, FT);
// NOTE: ReplaceType has issues with certain types, such as a class
// declaration like this: class C : D!C {}
// For this reason, we test first if it compiles and only then use it.
// It also replaces a type with the contained "alias this" type under
// certain conditions, so we make a second check to see heuristically
// if This is actually present in FT
//
// Phobos issues: 19696, 19697
static if (is(ReplaceType!(This, U, FT)) && !is(ReplaceType!(This, void, FT)))
alias TypeOf = ReplaceType!(This, U, FT);
else alias TypeOf = FT;
}
///