This Week in D September 4, 2016

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.

Statistics

In the community

Community announcements

See more at the announce forum.

Tip of the Week

This week's tip actually comes from Walter Bright himself: you can use identity template specializations with constraints to make a generic fallback.

import core.stdc.stdio;

void f(T:T)(T t) if(is(T == int)) { printf("case int\n"); }
void f(T:T)(T t) if(is(T == uint)) { printf("case uint\n"); }
void f(T)(T t) { printf("case default\n"); }

void main()
{
    f(1);
    f(1u);
    f(1.0);
}

This works because specializations are preferred over non-specializations, and T:T is the identity specialization.

(Adam writing again from here on) The problem of the fallback is normally that the compiler will complain that there are two equal matches for the template parameters. Constraints are a binary condition to take a template out of consideration entirely, but if it passes, if it is considered, it holds equal weight to all other passing options.

Specialization changes the weight of a template in consideration. When one is present and matching, that template gets a higher match weight than ones without or with a less specific specialization, overcoming the "multiple templates match" error - now they match, but since one is a higher weight, it isn't an equal match and thus not an error.

So, to use this technique, you use the T:T specialization on one of the arguments (it doesn't matter which one, all this does is increase the match weight without changing anything else, specializing to itself will only match itself anyway) for each of the better matches with the constraint. Then, the fallback is written without it.

Using a fallback and specialization from across modules may need local aliases to merge the overload sets. Consider the following:

module u;
void fallback(T)() {
        pragma(msg, "fallback");
}
module uu.d;
void fallback(T:T)() if(is(T == int)) {
        pragma(msg, "int");
}
import u;
import uu;

void main() {
        fallback!char;
}

Compiling those three modules together will result in the fallback message being printed, without error.

Change the main function to use fallback!int instead of char and recompile. Now, you'll get an error: uuu.d(5): Error: uu.fallback!int.fallback at uu.d(1) conflicts with u.fallback!int.fallback at u.d(3)

The reason for this error is that there's two matches in different modules, triggering D's hijacking rules. Both templates match, the if int one is NOT taken out of consideration, but since they are in different modules, the specialization doesn't automatically apply; despite the higher weight, the fact they are in separate modules still triggers the error, just like with function overloads.

Thankfully, the solution is the same as with function overloads too: you can alias the template from both modules together to explicitly resolve the problem:

import u;
import uu;

alias fallback = u.fallback;
alias fallback = uu.fallback;

void main() {
        fallback!int;
}

Now, both work! Alternatively, the module with the specialization can import the fallback and do the aliasing there:

import u; // note, you must import the module with the non-specialized fallback
// but it does NOT have to be a public import; the alias below is enough

alias fallback = u.fallback; // add the fallback to the overload set

void fallback(T:T)() if(is(T == int)) {
        pragma(msg, "int");
}

Alternatively, you can edit the fallback module to import the specialization module, and do the alias there. Either way though, one module must be aware of the other(s) to merge the overload sets.

Once that is done, import JUST the one module with the aliases (still importing both would trigger the ambiguity again) and compile - boom, it works!

I must confess, when Walter posted this, I was a bit jealous that I never considered this option before myself!

Learn more about D

To learn more about D and what's happening in D: