Mixin Macros Pattern

From D Wiki
Jump to: navigation, search

From David Simcha's D-Specific Design Patterns talk at DConf 2013.

Problem: Repetitive code with slight variations

Example: Arithmetic operator overloading

Want:

  • Minimal code duplication
  • Ability to use templates/CTFE
  • (Avoid (Lispy (syntax)))

Solution: Mixins + Compile-Time Magic

// D's operator overloading was designed around mixins
struct Int 
{
    Int opBinary(string op)(Int rhs)
    {
       int value = mixin("value_ " ~ op ~ " rhs.value_");
       return Int(value);
    }

    int value() @property 
    { 
        return value_; 
    }

    private int value_;
}

void main()
{
    assert((Int(2) - Int(5)).value == -3);
    assert((Int(2) + Int(4)).value == 7);
}

The way D's operating overloading works, is there's only one function to overload for all binary operators, as opposed to the C++ where each operator is it's own individual implementation. The function takes a string, that represents its operator, as a template parameter. For example, to implement the addition operator, the opBinary template will be instantiated with the "+" string, and to implement the subtraction operator, it will instantiated with the "-" string. The beauty of this is it enables reduction of boilerplate code with one mixin macro handling all binary operators.

In the example above, the lhs value (value_), the operator string (op), and the rhs value (rhs.value_) are appended to one another and then mixed in. When mixed in, the resulting string is evaluated at the current scope as regular code. In the example above it would be prudent to add a template constraint to verify that the operator is either a "+" or a "-" since those are the only implementations. It doesn't have the performance cost of runtime evaluation, and unless the build system is compromised there's no security risk since it's all evaluated at compile-time. It's using compile-time computations to essentially create macros.