Welcome to This Week in D! Each week, we'll summarize what's been going on in the D community and write brief advice columns to help you get the most out of the D Programming Language.
The D Programming Language is a general purpose programming language that offers modern convenience, modeling power, and native efficiency with a familiar C-style syntax.
This Week in D has an RSS feed.
This Week in D is edited by Adam D. Ruppe. Contact me with any questions, comments, or contributions.
See more at the announce forum.
Today's tip is courtesy of Richard Andrew Cattermole on the topic of bindable events.
So you want to have bindable events for your program, what do you do? Well naturally of course you jump into delegates and maybe a getter and setter for it as well. Or how about going the Java way with a heavy OOP approach? Well all of these are generally hard to work with and generally require manual registration processes with a side of boiler plate for the end user.
But we don't like boilerplate for our users or extra work now do we? So we look at reflection, well D doesn't do runtime reflection :) but it does do compile time reflection, however this does mean you need some form of container and to 'register' them. So sure you could use the 'get checker' pattern from Developing with compile time in mind but how about actually making it enjoyable for our end users? Well for that there is a little known template feature where by an argument of this T which initializes for 'real' instance type of a class. Which is wonderful when used for the constructor for a root class as it is required to be called from inherited classes.
So let's get to work with a nice example of this:
struct UDA { string text; } class Root { this(this T)() { import std.traits : getUDAs, hasUDA; foreach(m; __traits(allMembers, T)) { static if (hasUDA!(__traits(getMember, T, m), UDA)) { foreach(uda; getUDAs!(__traits(getMember, T, m), UDA)) { pragma(msg, m); pragma(msg, "\t" ~ uda.stringof); } } } } } class Child : Root { this() { super(); } @UDA("abcd") void func() {} } void main() { Child child = new Child; }
As you can see, the Child class does not have any form of mixin's string or otherwise. It doesn't declare that it is registering itself either. This is the beauty of this approach. It allows the root class to register the child class instance automatically for you. Making it very nice and easy to use for the end developer.
But of course what we would like to do is have multiple inheritance levels. Sadly this hits a bug with the this template argument.
So to around this you can utilise three constructors per inheritable class.
struct UDA { string text; } class Root { this(){} this(this T)() { __ctor!T; } this(T)() { import std.traits : getUDAs, hasUDA; pragma(msg, T); foreach(m; __traits(allMembers, T)) { static if (hasUDA!(__traits(getMember, T, m), UDA)) { foreach(uda; getUDAs!(__traits(getMember, T, m), UDA)) { pragma(msg, m); pragma(msg, "\t" ~ uda.stringof); } } } } } class Child : Root { this(this T)() {super. __ctor!T(); } this(T)() { super.__ctor!T(); } @UDA("abcd") void func() {} } class Child2 : Child { this() { super(); } @UDA("1234") void func2() {} } void main() { Child2 child = new Child2; }
You will notice a couple of differences beyond the reach of Child2. Instead of the usual one constructor, you will need three if it can be (after the first one) inherited from.
this(){} this(this T)() { super.__ctor!T; } this(T)() { super.__ctor!T(); }
Add with the appropriate arguments you also need two new templated constructors. The third empty constructor is required because super is not being called explicitly so the compiler adds an implicit call in. Sadly the three constructors are required as a workaround for bug 13635 but none the less useful and fairly easy to work with.
As an example of supporting arguments here it is:
struct UDA { string text; } class Root { this() {} this(this T)(int x) { __ctor!T(x); } this(T)(int x) { import std.traits : getUDAs, hasUDA; import std.stdio : writeln; pragma(msg, T); foreach(m; __traits(allMembers, T)) { static if (hasUDA!(__traits(getMember, T, m), UDA)) { foreach(uda; getUDAs!(__traits(getMember, T, m), UDA)) { pragma(msg, m); pragma(msg, "\t" ~ uda.stringof); } } } writeln(x); } } class Child : Root { this() {} this(this T)(int x) { super.__ctor!T(x); } this(T)(int x) { super.__ctor!T(x); } @UDA("abcd") void func() {} } class Child2 : Child { this(int x) { super(x); } @UDA("1234") void func2() {} } void main() { Child2 child = new Child2(3); }
To learn more about D and what's happening in D: