opDispatch & opApply

D allows overriding operators like +, - or the call operator () for classes and structs. We will have a closer look at the two special operator overloads opDispatch and opApply.

opDispatch

opDispatch can be defined as a member function of either struct or class types. Any unknown member function call to that type is passed to opDispatch, passing the unknown member function's name as a string template parameter. opDispatch is a catch-all member function and allows another level of generic programming - completely at compile time!

struct C {
    void callA(int i, int j) { ... }
    void callB(string s) { ... }
}
struct CallLogger(C) {
    C content;
    void opDispatch(string name, T...)(T vals) {
        writeln("called ", name);
        mixin("content." ~ name)(vals);
    }
}
CallLogger!C l;
l.callA(1, 2);
l.callB("ABC");

opApply

An alternative way to implementing a foreach traversal instead of defining a user defined range is to implement an opApply member function. Iterating with foreach over such a type will call opApply with a special delegate as a parameter:

class Tree {
    Tree lhs;
    Tree rhs;
    int opApply(int delegate(Tree) dg) {
        if (lhs && lhs.opApply(dg)) return 1;
        if (dg(this)) return 1;
        if (rhs && rhs.opApply(dg)) return 1;
        return 0;
    }
}
Tree tree = new Tree;
foreach(node; tree) {
    ...
}

The compiler transforms the foreach body to a special delegate that is passed to the object. Its one and only parameter will contain the current iteration's value. The magic int return value must be interpreted and if it is not 0, the iteration must be stopped.

In-depth

rdmd playground.d