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.

Reply via email to