bearophile wrote: > [multiple dispatch and what they use it for.] I wonder if we could do it in the library by using overloads and a generated dynamic cast.
class Base { // if the types are known, regular overloading does the job void multipleDispatchFun(A a, A b) { writeln("Called (A, A)"); } void multipleDispatchFun(A a, B b) { writeln("Called (A, B)"); } void multipleDispatchFun(B a, B b) { writeln("Called (B, B)"); } } class A : Base {} class B : Base {} void main() { Base base = new Base(); Base a = new A(); Base b = new B(); // doesn't compile - static types don't match base.multipleDispatchFun(a, b); // So, we'd have to cast it ourselves and try to call if(dynamic casts are ok) base.multipleDispatchFun(cast(A) a, cast(B) b); // would work } It's that last part that multiple dispatch solves (someone correct me if I'm wrong). It's a pain to do manually. But maybe D can do it automatically. We can generate another overload that takes the base class, tries to cast to all the existing overloads, and if not null, go ahead and call it. This isn't complete, but it works for this simple case so it's a start and proof of concept: ========= import std.traits; template implementMultipleDispatch(alias Class, alias method) { //pragma(msg, multipleDispatchImpl!(Class, method, // __traits(identifier, method))()); // mixing it in right here didn't work for some reason alias multipleDispatchImpl!(Class, method, __traits(identifier, method)) implementMultipleDispatch; } // straight stringof hit a problem with forward reference, so this hack will // do it string[] parameterTypeStrings(string methodString)() { string[] ret; int startingAt; int state = 0; foreach(idx, c; methodString) { switch(state) { // first, we find the opening param case 0: if(c == '(') { state++; startingAt = idx + 1; } break; case 1: // we're reading a type name if(c == ' ') { // just finished ret ~= methodString[startingAt .. idx]; state = 2; } break; case 2: // now we're reading a name if(c == ' ') { state = 1; // another type startingAt = idx + 1; } if(c == ')') // we're done //break loop; state = 3; break; // we're done, but break loop doesn't work in CTFT case 3: // do nothing with the rest } } return ret; } string multipleDispatchImpl(alias Class, alias method, string methodName)() { string code = `void ` ~ methodName ~ `(`; alias typeof(__traits(getOverloads, Class, methodName)) overloads; string baseType = Class.stringof; bool outputted = false; // we'll reuse the call string to make our calls string call = methodName ~ "("; char arg_char = 'a'; foreach(arg; ParameterTypeTuple!(method)) { if(outputted) { code ~= ", "; call ~= ", "; } else outputted = true; code ~= baseType ~ " " ~ arg_char; call ~= " " ~ arg_char ~ "_casted"; arg_char++; } call ~= ");"; code ~= ") {"; foreach(overload; overloads) { code ~= "{"; // we'll introduce a scope to do our casts string ifCheck; // this lists the checks for null outputted = false; arg_char = 'a'; foreach(argument; parameterTypeStrings!(overload.stringof)) { // just try to cast all of them code ~= "auto "; code ~= arg_char ~ "_casted = cast("; code ~= argument; code ~= ") " ~ arg_char ~ ";"; if(outputted) ifCheck ~= " && "; else outputted = true; ifCheck ~= arg_char ~ "_casted !is null"; arg_char++; } // if none of the args are null, it's safe to do the call code ~= "if(" ~ ifCheck ~ ") {"; code ~= call ~ "return;"; // return because we're done code ~= "}"; // end if code ~= "}"; // close our casting scope } code ~= "}"; return code; } ============== Here's a test using it: import std.stdio; // copy paste the above in here class A : Base {} class B : Base {} class Base { void multipleDispatchFun(A a, A b) { writeln("Called (A, A)"); } void multipleDispatchFun(A a, B b) { writeln("CORRECT Called (A, B)"); } void multipleDispatchFun(B a, B b) { writeln("Called (B, B)"); } mixin (implementMultipleDispatch!(typeof(this), multipleDispatchFun)); } void main() { Base base = new Base(); Base a = new A(); Base b = new B(); // calls the generated base class overload base.multipleDispatchFun(a, b); // and, of course, if the static types are known, the // regular overload works fine. only problem is if one // is statically known and one is not. Then you shoud // cast to the base type manually so it triggers the // generated overload } ======== This is a toy example, but if someone really wanted to spend the time, I think it proves it can be done for real examples too, at least in theory.