// released into the public domain module multipledispatch; struct DispatchArg(int i) { static assert (i >= 0); alias i argNr; } // passed to the MultipleDispatch template to mark dispatch params alias DispatchArg!(0) _0; alias DispatchArg!(1) _1; alias DispatchArg!(2) _2; alias DispatchArg!(3) _3; alias DispatchArg!(4) _4; alias DispatchArg!(5) _5; alias DispatchArg!(6) _6; alias DispatchArg!(7) _7; alias DispatchArg!(8) _8; alias DispatchArg!(9) _9; class MethodNotFoundException : Exception { this(string msg) { super(msg); } } // finds the number of inheritance 'steps' that lie between derived and base // if derived is not derived from base, uint.max is returned private uint getInheritanceDistance(ClassInfo derived, ClassInfo base) { uint dist = 0; while(derived !is base) { ++dist; derived = derived.base; if(derived is Object.classinfo) return uint.max; } return dist; } // takes the tuple args[0..len] and replaces the positions // indicated by the DispatchArgs args[len..$] with the type Object private template ReplaceWithObject(size_t len, args...) { static if(len == args.length) alias args ReplaceWithObject; else alias ReplaceWithObject!(len, args[0..args[len].argNr], Object, args[args[len].argNr+1..len], args[len+1..$]) ReplaceWithObject; } template MultipleDispatch(string fname, dispatch_args...) { // get argument list and return type of first function with name fname static if(mixin("is(typeof(" ~ fname ~ ") first_arg_types == function) && is(typeof(" ~ fname ~ ") return_type == return)")) { // the dispatch function has an argument list where the arguments // listed as dispatch_args are Objects return_type dispatch(ReplaceWithObject!(first_arg_types.length, first_arg_types, dispatch_args) params) { alias ReplaceWithObject!(first_arg_types.length, first_arg_types, dispatch_args) arg_types; alias return_type delegate(arg_types) dg_type; // iterate over each function of name fname alias typeof(__traits(getVirtualFunctions, typeof(this), fname)) funcs; next_func: foreach(i, func; funcs) { // get the return type and argument list of this particular overload static if(is(func it_arg_types == function) && is(func it_return_type == return)) { static assert(is(arg_types == ReplaceWithObject!(it_arg_types.length, it_arg_types, dispatch_args)) && is(return_type == it_return_type), "Except for the arguments dispatched by, the signature of all functions must be identical."); foreach(d_arg; dispatch_args) static assert(is(it_arg_types[d_arg.argNr] == class), "Arguments dispatched by must be of class type."); // only continue if all the classes given to dispatch match the type of the classes // expected by the current function foreach(d_arg; dispatch_args) if(it_arg_types[d_arg.argNr].classinfo !is params[d_arg.argNr].classinfo) continue next_func; // we have a match, call it return (cast(dg_type) &__traits(getVirtualFunctions, this, fname)[i])(params); } else static assert(false, fname ~ " is not a function"); } // construct the no-exact-match error message char[] error_msg = "No method found in " ~ this.classinfo.name ~ " that matches the signature " ~ typeid(return_type).toString ~ " " ~ fname ~ "("; next_param: foreach(i, p; params) { foreach(d_arg; dispatch_args) if(i == d_arg.argNr) { error_msg ~= p.classinfo.name ~ ", "; continue next_param; } error_msg ~= typeid(arg_types[i]).toString ~ ", "; } error_msg[$-2] = ')'; throw new MethodNotFoundException(error_msg); } } else static assert(false, fname ~ " is not a function"); } template MultipleDispatchFallthrough(string fname, dispatch_args...) { // get argument list and return type of first function with name fname static if(mixin("is(typeof(" ~ fname ~ ") first_arg_types == function) && is(typeof(" ~ fname ~ ") return_type == return)")) { // the dispatch function has an argument list where the arguments // listed as dispatch_args are Objects return_type dispatch(ReplaceWithObject!(first_arg_types.length, first_arg_types, dispatch_args) params) { alias ReplaceWithObject!(first_arg_types.length, first_arg_types, dispatch_args) arg_types; alias return_type delegate(arg_types) dg_type; // initialize structure that'll hold the best match we've found struct BestMatch { uint dist[dispatch_args.length]; dg_type dg; bool ambigious = false; } BestMatch best_match; for(size_t i = 0; i < dispatch_args.length; ++i) best_match.dist[i] = uint.max; // iterate over each function of name fname alias typeof(__traits(getVirtualFunctions, typeof(this), fname)) funcs; next_func: foreach(i, func; funcs) { // get the return type and argument list of this particular overload static if(is(func it_arg_types == function) && is(func it_return_type == return)) { static assert(is(arg_types == ReplaceWithObject!(it_arg_types.length, it_arg_types, dispatch_args)) && is(return_type == it_return_type), "Except for the arguments dispatched by, the signature of all functions must be identical."); foreach(d_arg; dispatch_args) static assert(is(it_arg_types[d_arg.argNr] == class), "Arguments dispatched by must be of class type."); // if one of the classes given to dispatch doesn't match the type of the classes // expected by the current function, check if it is a good match and continue with next foreach(d_arg; dispatch_args) if(it_arg_types[d_arg.argNr].classinfo !is params[d_arg.argNr].classinfo) { uint odist[dispatch_args.length]; bool better = false, worse = false; foreach(j, inner_d_arg; dispatch_args) { odist[j] = getInheritanceDistance(params[inner_d_arg.argNr].classinfo, it_arg_types[inner_d_arg.argNr].classinfo); if(odist[j] == uint.max) continue next_func; else if(odist[j] < best_match.dist[j]) better = true; else if(odist[j] > best_match.dist[j]) worse = true; } if(better == true && worse == false) { best_match.dist[] = odist[]; best_match.dg = cast(dg_type) &__traits(getVirtualFunctions, this, fname)[i]; } else if(better == true && worse == true) best_match.ambigious = true; continue next_func; } // we have an exact match, call it return (cast(dg_type) &__traits(getVirtualFunctions, this, fname)[i])(params); } else static assert(false, fname ~ " is not a function"); } if(best_match.ambigious) throw new Exception("Ambigious resolution"); return best_match.dg(params); } } else static assert(false, fname ~ " is not a function"); }