1 module util.visitor; 2 3 auto dispatch(alias unhandled = function void(t) { 4 throw new Exception(typeid(t).toString() ~ " is not supported."); 5 // XXX: Buggy for some reason. 6 // throw new Exception(typeid(t).toString() ~ " is not supported by visitor " ~ typeid(V).toString() ~ " ."); 7 }, V, T, Args...)(ref V visitor, Args args, T t) 8 if (is(V == struct) && (is(T == class) || is(T == interface))) { 9 return dispatchImpl!(unhandled)(visitor, args, t); 10 } 11 12 auto dispatch(alias unhandled = function void(t) { 13 throw new Exception(typeid(t).toString() ~ " is not supported."); 14 // XXX: Buggy for some reason. 15 // throw new Exception(typeid(t).toString() ~ " is not supported by visitor " ~ typeid(V).toString() ~ " ."); 16 }, V, T, Args...)(V visitor, Args args, T t) 17 if ((is(V == class) || is(V == interface)) 18 && (is(T == class) || is(T == interface))) { 19 return dispatchImpl!(unhandled)(visitor, args, t); 20 } 21 22 // XXX: is @trusted if visitor.visit is @safe . 23 private auto dispatchImpl(alias unhandled, V, T, Args...)(auto ref V visitor, 24 Args args, T t) in { 25 assert(t, "You can't dispatch null"); 26 } do { 27 static if (is(T == class)) { 28 alias o = t; 29 } else { 30 auto o = cast(Object) t; 31 } 32 33 auto tid = typeid(o); 34 35 import std.traits; 36 static if (is(V == struct)) { 37 import std.typetuple; 38 alias Members = TypeTuple!(__traits(getOverloads, V, "visit")); 39 } else { 40 alias Members = MemberFunctionsTuple!(V, "visit"); 41 } 42 43 foreach (visit; Members) { 44 alias parameters = ParameterTypeTuple!visit; 45 46 static if (parameters.length == args.length + 1) { 47 alias parameter = parameters[args.length]; 48 49 // FIXME: ensure call is correctly done when args exists. 50 static if (is(parameter == class) 51 && !__traits(isAbstractClass, parameter) 52 && is(parameter : T)) { 53 if (tid is typeid(parameter)) { 54 return visitor.visit(args, () @trusted { 55 // Fast cast can be trusted in this case, we already did the check. 56 import util.fastcast; 57 return fastCast!parameter(o); 58 }()); 59 } 60 } 61 } 62 } 63 64 // Dispatch isn't possible. 65 enum returnVoid = is(typeof(return) == void); 66 static if (returnVoid || is(typeof(unhandled(t)) == void)) { 67 unhandled(t); 68 assert(returnVoid); 69 } else { 70 return unhandled(t); 71 } 72 } 73 74 auto accept(T, V)(T t, ref V visitor) 75 if (is(V == struct) && (is(T == class) || is(T == interface))) { 76 return acceptImpl(t, visitor); 77 } 78 79 auto accept(T, V)(T t, V visitor) 80 if ((is(V == class) || is(V == interface)) 81 && (is(T == class) || is(T == interface))) { 82 return acceptImpl(t, visitor); 83 } 84 85 private auto acceptImpl(T, V)(T t, auto ref V visitor) { 86 static if (is(typeof(visitor.visit(t)))) { 87 return visitor.visit(t); 88 } else { 89 visitor.dispatch(t); 90 } 91 }