American in Spain

Useful D Templates

February 5, 2007

Recently, I've been experimenting with the new D programming language. One thing that's really amazed me is the power of the templating system. Here are three code-saving templates to help avoid the deadly programming sin of code duplication.

Generic opEquals and opCmp

In D, if you want your objects to be comparable with the ==, < , <=, >, or >= operators, you have to implement opEquals(Object) and opCmp(Object) that take the generic Object type. This is annoying, because it forces you to have to do type checking in every single one of your opEquals and opCmp methods in each class. I have devised a solution.

template GenericEquals(T) {   public int opEquals(Object o)   {     if(this is o)       return true;     T that = cast(T) o;     if(that is null)  // catches failed casts       return false;     else       return this.opEquals(that);   } }

...and...

template GenericCmp(T) {   int opCmp(Object o)   {     if(this is o)       return 0;     T that = cast(T) o;     if(that is null)  // catches failed casts       return 1;     else       return this.opCmp(that);   } }

Then, you can define opEquals and opCmp in your class with the right type of parameter and include the generic forms with a mixin!

class MyClass {   private MyField myField;      mixin GenericEquals!(MyClass);   public int opEquals(MyClass that)   {     return this.myField == that.myField;   }      mixin GenericCmp!(MyClass);   public int opCmp(MyClass that)   {     if(this.myField > that.myField)       return 1;     else if(this.myField < that.myField)       return -1;     else       return 0;   } }

But look at that opCmp(MyClass) method? Gross! Can't we remove some of that logic into a template too? Yes, we can! Presenting...

Generic compareFields Template

When you have more than one field that needs to be compared to check for equality and natural ordering of your class, your opCmp method can get pretty ugly and repetitive. For example:

class MyClass {   private char[] name;   private int age;      public int opCmp(MyClass that)   {     if(this.name > that.name)       return 1;     else if(this.name < that.name)       return -1;     else if(this.age > that.age)       return 1;     else if(this.age < that.age)       return -1;     else       return 0;   } }

I've written a compareFields() templated method that will do this for you. But the true beauty of the method is that it is completely type-safe at compile time. It requires an even number of parameters and every pair of parameters have to be of the same type. Behold!

int compareFields(T, R...)(T a, T b, R rest) {   if(a > b)     return 1;   else if(a < b)     return -1;   static if(rest.length)   {     static assert(rest.length > 1);     return compareFields(rest);   }   else     return 0; }

The method to compare both name and age member variables now becomes:

class MyClass {   private char[] name;   private int age;      public int opCmp(MyClass that)   {     return compareFields(this.name, that.name,                          this.age, that.age);   } }

If I leave off a parameter, or accidentally pass "this.name, that.name, this.age, that.name", it will fail because the second pair of parameters are not of the same type. And again, all this happens at compile time!

This is all made possible by D's unique variadic templates. Enjoy!