module MultiDisFunction; import std.stdio; import meta.Util; alias itoa intToString; import meta.Tuple; import meta.Apply; import meta.FuncMeta; import MDF_usercode; /***** META UTILS *****/ template isFunctionType(T) { const bool isFunctionType = is(typeof(*T) == function); } template isImFunctionType(T) { const bool isFunctionType = is(typeof(T) == function); } /** * Instantiate TPL!(n), n times, from 0 to n. */ template staticIter(int n, alias TPL) { static if(n >= 0) { // cannot just instantiate the template, must create some entity: alias staticIter!(n-1, TPL) _DUMMY2; alias TPL!(n) _DUMMY; } } /***** MULTIDISPATCH *****/ /** * Need this struct just because static arrays are not proper value types :( */ struct ClassInfoVec(int N) { ClassInfo[N] ciar; int opCmp(ClassInfoVec* civec) { return ciar == civec.ciar; } char[] toString() { char[] result; foreach( ci; ciar) { result ~= ci.name ~ ", "; } result[$-2] = ']'; return "[" ~ result; } } // This template was to be in the MultiDispatchFunction.AddDispatch function. // But that is not allowed, nor even in the struct MultiDispatchFunction template AddDispatch_typeCheck(alias A, alias B) { template typeCheckN(int n) { // This to to ensure A&B can be instantiated ... alias A!(n) _TEST_A; alias B!(n) _TEST_B; // ... as the is-expression won't complain: static assert(is(A!(n) : B!(n)), "Argument " ~ intToString!(n) ~ " is not covariant"); } } // Another workaround like above. // Generates code for a runtime ClassInfo array, from compile time info template AddDispatch_convConstCiToDyn(alias A) { void AddDispatch_convConstCiToDyn(int n) (ClassInfo[] ar) { static if( n > 0 ) AddDispatch_convConstCiToDyn!(n-1) (ar); //recurse ar[n] = (new A!(n)).classinfo; } } // Another workaround like above. // Generates arguments (as tuples) for a parameterized function call. // Could maybe be cleaned up a bit. // It would be nice to have auto for return types. template call_genArgsTuple(alias TYPEARR, int n) { static if( n > 0 ) { alias .call_genArgsTuple!(TYPEARR, n-1).genArgsTuple genfunc; typeof( (genfunc(null)).mix.append!(TYPEARR!(n)) (cast(TYPEARR!(n)) null) ) genArgsTuple(Object[] objar) { auto tuple = genfunc(objar); return tuple.mix.append!(TYPEARR!(n))(cast(TYPEARR!(n)) objar[n] ); } } else { typeof( (cast(EmptyTuple) null).mix.append!(TYPEARR!(n)) (cast(TYPEARR!(n)) null) ) genArgsTuple(Object[] objar) { EmptyTuple tuple; return tuple.mix.append!(TYPEARR!(n))(cast(TYPEARR!(n)) objar[n] ); } } } /** * The Multiple Dispatch Dispatcher */ struct MultiDispatchFunction(MDFT) { static assert(isFunctionType!(MDFT), "Error not a function"); alias funcInfoT!(MDFT) funcTypeINFO; // The type of the MDFunction alias ClassInfoVec!(funcTypeINFO.numArgs) ClassInfoVecN; MDFT[ClassInfoVecN] dispatches; // Map of each dispatch. /** * Adds a new dispatch, does some checks */ void AddDispatch(FT) (FT dispatchfn) { static assert(isFunctionType!(FT), "Error not a function"); // Check if the delegate is of a compatible type alias funcInfoT!(FT) newfuncTypeInfo; static assert(is(funcTypeINFO.RetType == newfuncTypeInfo.RetType), "Dispatch has different return type"); static assert(funcTypeINFO.numArgs == newfuncTypeInfo.numArgs, "Dispatch has different num of arguments"); // Parameter compatibility check. // it is a hack because I can't put template typeCheck here alias AddDispatch_typeCheck!(newfuncTypeInfo.ArgType, funcTypeINFO.ArgType) tc; // foreach numArgs instantiate tc.typeCheckN alias staticIter!(funcTypeINFO.numArgs-1, tc.typeCheckN) _DUMMY; // Add the function to the dispatch list: ClassInfoVecN classinfovec; alias AddDispatch_convConstCiToDyn!(newfuncTypeInfo.ArgType) tmpl_tmp; tmpl_tmp!(funcTypeINFO.numArgs-1) (classinfovec.ciar); // The cast is safe, since the function args are covariant dispatches[classinfovec] = cast(MDFT) dispatchfn; writefln("Dispatch Added: ", classinfovec.toString()); } /** * The dispatcher function which selects the appropriate dispatch * (exact class match only) */ funcTypeINFO.RetType call(Object[] objar ...) { writefln( "== MDF called =="); writefln( "Number of dispatches: ", dispatches.length); foreach(cinfovec, dispatchfn; dispatches) { writefln( "Comparing to Dispatch: ", cinfovec.toString() ); bool match = true; foreach(i, ci; cinfovec.ciar) { if(ci != objar[i].classinfo) match = false; } if( match ) { writefln( ">> Dispatch match found! "); const len = funcTypeINFO.numArgs-1; alias call_genArgsTuple!(funcTypeINFO.ArgType, len) tmp; auto args = tmp.genArgsTuple(objar); return apply(dispatchfn, args); } } throw new MultiDisException("No Dispatch Found"); } static class MultiDisException : Exception { this(char[] msg) { super(msg); } } }