From 8fba150b035d185c11d802a9b0e9b90fc10d0ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Fri, 14 Aug 2015 11:24:54 +0200 Subject: [PATCH] Add limited support for operations where the RHS is a TaggedAlgebraic. --- source/taggedalgebraic.d | 60 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/source/taggedalgebraic.d b/source/taggedalgebraic.d index 9d91d52..ffe5b6d 100644 --- a/source/taggedalgebraic.d +++ b/source/taggedalgebraic.d @@ -442,14 +442,23 @@ unittest { // multiple operator choices assert(ta + 10.5 == 22.5); } -/*unittest { +unittest { // Binary op between two TaggedAlgebraic values union U { int i; } alias TA = TaggedAlgebraic!U; TA a = 1, b = 2; - assert(a + b == 3); static assert(is(typeof(a + b) == int)); -}*/ + assert(a + b == 3); +} + +unittest { // Ambiguous binary op between two TaggedAlgebraic values + union U { int i; double d; } + alias TA = TaggedAlgebraic!U; + + TA a = 1, b = 2; + static assert(is(typeof(a + b) == TA)); + assert(a + b == 3); +} /// Convenience type that can be used for union fields that have no value (`void` is not allowed). struct Void {} @@ -482,7 +491,7 @@ unittest { } -private static auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) +private auto performOpRaw(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) { static if (kind == OpKind.binary) return mixin("value "~name~" args[0]"); else static if (kind == OpKind.unary) return mixin("name "~value); @@ -496,8 +505,51 @@ private static auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T val unittest { union U { int i; string s; } + { int v = 1; assert(performOpRaw!(U, OpKind.binary, "+")(v, 3) == 4); } + { string v = "foo"; assert(performOpRaw!(U, OpKind.binary, "~")(v, "bar") == "foobar"); } +} + + +private auto performOp(U, OpKind kind, string name, T, ARGS...)(ref T value, /*auto ref*/ ARGS args) +{ + import std.traits : isInstanceOf; + static if (ARGS.length > 0 && isInstanceOf!(TaggedAlgebraic, ARGS[0])) { + static if (is(typeof(performOpRaw!(U, kind, name, T, ARGS)(value, args)))) { + return performOpRaw!(U, kind, name, T, ARGS)(value, args); + } else { + alias TA = ARGS[0]; + template MTypesImpl(size_t i) { + static if (i < TA.FieldTypes.length) { + alias FT = TA.FieldTypes[i]; + static if (is(typeof(&performOpRaw!(U, kind, name, T, FT, ARGS[1 .. $])))) + alias MTypesImpl = TypeTuple!(FT, MTypesImpl!(i+1)); + else alias MTypesImpl = TypeTuple!(MTypesImpl!(i+1)); + } else alias MTypesImpl = TypeTuple!(); + } + alias MTypes = NoDuplicates!(MTypesImpl!0); + static assert(MTypes.length > 0, "No type of the TaggedAlgebraic parameter matches any function declaration."); + static if (MTypes.length == 1) { + if (args[0].hasType!(MTypes[0])) + return performOpRaw!(U, kind, name)(value, args[0].get!(MTypes[0]), args[1 .. $]); + } else { + // TODO: allow all return types (fall back to Algebraic or Variant) + foreach (FT; MTypes) { + if (args[0].hasType!FT) + return ARGS[0](performOpRaw!(U, kind, name)(value, args[0].get!FT, args[1 .. $])); + } + } + throw new /*InvalidAgument*/Exception("Algebraic parameter type mismatch"); + } + } else return performOpRaw!(U, kind, name, T, ARGS)(value, args); +} + +unittest { + union U { int i; double d; string s; } + { int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, 3) == 4); } { string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, "bar") == "foobar"); } + { string v = "foo"; assert(performOp!(U, OpKind.binary, "~")(v, TaggedAlgebraic!U("bar")) == "foobar"); } + { int v = 1; assert(performOp!(U, OpKind.binary, "+")(v, TaggedAlgebraic!U(3)) == 4); } }