Search code examples
operator-overloadingddmdgdc

Elegant operator overloading in D


For a while I was confused about the direction of D's operator overloading, but now I realize it's a beautiful system... if It would only work with core types (int, float, etc). Consider the follow code:

struct Vector {
    float X, Y;

    void opOpAssign(string op)(Vector vector) {
        X.opOpAssign!op(vector.X); // ERROR: no property "opOpAssign" for float
        Y.opOpAssign!op(vector.Y); // ERROR: ditto
    }
}

This would be beautiful code if it worked, seeing as it overloads all +=, -=, *=, etc.. operators in one method. However, as you can see, it doesn't work out of the box. I have created a solution using templates (god I love D):

template Op(string op, T) {
    void Assign(ref T a, T b) {
        static if (op == "+") a += b;
          else if (op == "-") a -= b;
          else if (op == "*") a *= b;
          else if (op == "/") a /= b;
    }
}

struct Vector {
    float X, Y;

    void opOpAssign(string op)(Vector vector) {
        Op!(op, typeof(X)).Assign(X, vector.X);
        Op!(op, typeof(Y)).Assign(Y, vector.Y);
    }
}

This is fine, only I'd much prefer to keep everything "in house". Is there a way to make this work without the aid of a template? I know I'm being picky here, seeing as there's no performance loss and it's not hard to import a module in situation I need to do this. I'm just wondering if it's built in and I'm overlooking something.


Solution

  • Almost all overloaded operators in D are templates by definition. Notice that void opOpAssign(string op)(Vector vector) has a template parameter which is a string. So, no you can't overload it as a non-template function. Now, you don't need a second template to do it (so if by asking whether you need a template, you mean a helper template, then the answer is no), but the overloaded operator function is already a template.

    The canonical way to do what you you're trying to do here is to use string mixins:

    void opOpAssign(string op)(Vector vector)
    {
        mixin("X" ~ op ~ "=vector.X;");
        mixin("Y" ~ op ~ "=vector.Y;");
    }