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.
Release D 2.068.2 came out this week. It has regression fixes from the previous release.
See more at digitalmars.D.announce.
The simple tutorial on deploying Vibe.d to Azure Web App wasn't the only interesting tutorial on the forums this week. There was also a post on building Windows kernel-mode drivers in D
The author described how to build a Windows driver in D, pointing out a solution to a checksum problem, and plans to add more to it. As it is though, it proves the concept works!
This week, I want to talk about using UDAs and private implementations to generate modified public functions.
UDAs (user-defined attributes) in D can only attach data to a declaration. They cannot transform code on their own. Thus, you need to use some kind of annotated input to generate the modified output. Sometimes, the generated output is is a class which is used through a static interface, or network implementation code for remote procedure calls. These are fairly straightforward because you don't need to call the generated code directly - you can use the ordinary interface and perhaps a factory function.
But, sometimes you want the source to be practically hidden and the entire interface in your code to be modified. There's a few ways to do this, but all are basically the same: you write input declarations then mixin some transformed code.
The class and interface based implementation option works here too - you could write a class that wraps another class and exposes the same interface. But I want to show something more unique to D today: using template mixins as the source.
First, the example code. This will define a UDA called log and a SourceImpl source implementation that uses it. The ProcessLogging function will generate the instrumented functions, which are mixed in at the top level to expose them as the public interface. Finally, the main function shows that it will work.
enum log; mixin template SourceImpl() { int a; void foo() { import std.stdio; writeln(a); } @log void logMe() { a = 10; } } mixin template ProcessLogging(alias Source) { mixin Source _sourceMixin; mixin(_implementationStringMixin()); private string _implementationStringMixin() { string code; code ~= "static import std.traits;"; import std.traits; foreach(memberName; __traits(allMembers, _sourceMixin)) { static if(hasUDA!(__traits(getMember, _sourceMixin, memberName), log)) { string f = "__traits(getMember, _sourceMixin, \""~memberName~"\")"; code ~= "std.traits.ReturnType!("~f~") "~memberName~"(std.traits.ParameterTypeTuple!("~f~") args) { import std.stdio; writeln(\"Logging " ~ memberName ~"\"); return _sourceMixin." ~ memberName ~ "(args); }"; } } return code; } } mixin ProcessLogging!SourceImpl; void main() { logMe(); foo(); import std.stdio; writeln(a); }
The implementation of SourceImpl is fairly straightforward: it is just the ordinary declarations we would write anyway. mixin ProcessLogging!SourceImpl will do the transformations of it. The part that needs more explanation is the ProcessLogging function itself.
The first interesting piece is the mixin Source _sourceMixin; line. This starts things off by bringing in the original declarations as-is. This means anything in there that will not be instrumented will be made automatically available - no additional wrapping code needs to be written or generated. You'd want to do something similar to this inside each scope that needs transformation: do it at the top-level of a module or inside structs and classes that need changing too.
The advantage of using a mixin template for this is that all types of things will be automatically working out of the box - you don't need to write forwarding code for the various types of declarations yourself. The other advantage is they can be overridden by name, which we do next.
The _implementationStringMixin function generates the instrumented functions. (A more complex implementation could also handle more than just functions too.) For an explanation of why I used static imported traits inside the strings instead of .stringof, see last week' tip.
This function goes down the list of members with __traits(allMembers), checking for the attribute we're interested in with std.traits.hasUDA. If it is there, it generates a code string for a wrapper function - using the reflection traits in the string itself to forward values (BTW it should also probably forward various qualifiers like pure and @safe in a complete implementation - there are traits available for this too, though it can be a little tedious to write. Alternatively, it could generate templates that infer those annotations automatically) and inject the modified code.
Then, it uses the named mixin to call the originally written function in the return _sourceMixin." ~ memberName ~ "(args); portion.
Since template mixins allow you to override a symbol by name while still referring to the old version with the namespaced symbol, this means we will transform just the functions we want to transform while letting the compiler forward the others.
There's various other ways to implement function transforming in D. Another I like is to have a private implementation module and a public interface module containing the generated code. But this technique here brings together a couple nice features - mixin templates and UDAs - in a way that I think highlights benefits of both well.
To learn more about D and what's happening in D: