Learning D Part 2: Random Little Things I Like
by Malte Skarupke
D is better than C++ in many ways. Here are a couple examples that I have encountered so far.
Let’s start with the scope keyword:
void main() { OSInterface.Load(); scope(exit) OSInterface.Quit(); //... }
What this means is “at the end of the current scope, run OSInterface.Quit();. Meaning I can put the call to freeing resources right next to the line where I allocate it. There is also scope(failure) which only runs the when there is an exception, and scope(success) which only runs when the scope exits normally. This is a much better solution than try {} catch {} finally {}.
Another cool thing is lazy evaluation, for example in the function std.exception.enforce, which has this signature:
T enforce(T)(T value, lazy const(char)[] msg = null, string file = __FILE__, size_t line = __LINE__)
Which you use like this:
SDL_GLContext context = enforce(SDL_GL_CreateContext(window), "Failed to create OpenGL context: " ~ to!string(SDL_GetError()));
There are a couple brilliant things about this
- The function signature. This is how you declare a template function. It has two argument lists. The first is the list of template arguments, the second is the list of function arguments. Much better syntax than templates in C++.
- lazy const char[] msg. This means that the second argument only gets evaluated when there actually is an error. So the calls to SDL_GetError() and the string concatenation only happen if there is a problem. If nothing is wrong, the code continues to run normally.
- The __FILE__ and __LINE__ declarations in the signature: This is evaluated where the function is used, not where the function is declared. Which is much better than the C++ way. This actually makes __FILE__ and __LINE__ useful outside of macros.
The next cool thing is D’s approach to contract programming:
Each function can have an in{} and out{} block before the actual function body in which you can assert pre- and post-conditions.
class Shader { this(uint type, string text) in { assert(text.length); checkGlError(); } out { assert(shader); checkGlError(); } body { shader = glCreateShader(type); // ... } //... }
The function checkGlError is a function I wrote which asserts that glGetError() == GL_NO_ERROR. It also prints good error messages using the __FILE__ and __LINE__ special values from the previous example.
This can make your code much more readable. Because of this function bodies tend to contain much less error checking. Instead they just look like they do the functionality that you want them to do. You can obviously also strip out the in and out blocks in release builds if you want.
There is some unexpected, but good behavior in how DMD uses the stack:
void main() { class Class { this(int i) { this.i = i; } int i; } Class b; { scope Class a = new Class(1); b = a; writeln(a.i); writeln(b.i); } { scope Class a = new Class(2); writeln(a.i); writeln(b.i); } }
This will print “1 1 2 0”. No heap allocations happen in this, everything is on the stack. The reason for why it doesn’t print “1 1 2 2” (as it would in C++) is that the second scope gets different space on the stack than the first scope. Which is great and will prevent bugs. D also cleans up when objects are destroyed. It resets all members to default values, which is 0 for pointers and integers, and NAN for floats. You can specify a different default value by writing for example “int i = 5;” in line 6. Then this would print “1 1 2 5”.
Another cool thing is D’s implementation of the observer pattern. It’s one of the cleanest I’ve seen:
import std.signals; class Input { enum Button { /*...*/ } mixin Signal!(Button) onButtonDown; mixin Signal!(Button) onButtonUp; void handleEvent(in SDL_Event event) { switch(event.type) { case SDL_KEYDOWN: onButtonDown.emit(keyboardEventToButton(event)); break; case SDL_KEYUP: onButtonUp.emit(keyboardEventToButton(event)); break; //... } } //... } void main() { //... bool running = true; input.onButtonDown.connect((Input.Button b) { if (b == Input.Button.Escape) running = false; }); while(running) { //... } }
I have recently done some Java programming, and you have no idea how much of an improvement this is over Java. In C++ you could do something similar, but your best solution will probably have a mess of template specializations for different possible listeners. Here is a complete implementation of the above signal/slot mechanism:
mixin template Signal(T...) { alias void delegate(T) slot_t; final void emit(T i) { foreach (slot; slots) slot(i); } final void connect(slot_t slot) { slots ~= slot; // push_back(slot); } final void disconnect(slot_t slot) { for (size_t i = 0; i < slots.length; ) { if (slots[i] == slot) { slots[i] = slots[$ - 1]; // the $ stands for "length" inside // of array brackets slots = slots[0 .. $ - 1]; // pop_back() } else ++i; } } private: slot_t[] slots; // the slots to call from emit() }
Just look at that. That is so amazingly clean, a similar implementation in C++ would look ridiculous in comparison.
The delegate is a clever construct. It encapsulates everything that can be called. For member functions it stores both the “this” pointer and the function pointer.
The array operations are also a bit more elegant. The pop_back line is actually creating a slice into the existing array. The slice just happens to be one element shorter. I have looked at the disassembly and this is as efficient as the C++ pop_back implementation.
This implementation would be a bit annoying because of the garbage collector. You would have to always disconnect manually. (and you can not do that in the destructor because your object would never get collected as long as it’s still connected to a slot) So the real std.signals does a bit of hacking to get around that. I actually had to implement my own version because I couldn’t accept the hacking in std.signals. I’ll talk about the garbage collector and the mess it creates in another post, though.
One final cool thing is the built in support for unit tests. In any file you can just write, for example
unittest { mixin Signal!() signal; bool fired = false; signal.connect({ fired = true; }); assert(!fired); signal.emit(); assert(fired); }
And you can tell the compiler to either enable these or not. If you tell it to enable them, they will run before main() is called. I’ve seen similarly easy unit test setups for C++, yet you never use them for your small test projects, because you have to set up libraries. Having them integrated in the language makes it trivial to use them. In fact I have found myself writing all my code in which I try things as unit tests. Where normally you would try your code by setting up a simple example in main() and seeing if it runs, here it is actually less work to do so in a unit test. It’s perfect.
I have already discovered more cool things. For example the D equivalent to the preprocessor is much better, and there are nice things like pure functions. And I like how D treats structs and classes very differently from another. But this is long enough for now.
Wait, what? I’m trying to think of any other language, static or dynamic, that has that scoping behavior. Entering a curly brace resets non-scoped variables to their defaults? But only if it’s a standalone curly brace, not part of a compound statement? I’m confused by that…
Oh gosh, ~= overloaded to mean “push a delegate”? At least C# used +=.
Ah sorry, I didn’t explain that: Classes in D are always references. So when I assign “b = a;” that is a pointer assignment. I’m making ‘b’ point to an object that is allocated on the stack. At scope exit that object gets destroyed, but ‘b’ is still pointing to it. I may rewrite that example with structs, which behave more like in C++. For structs I’d have to make ‘b’ a pointer and take the address of ‘a’ to get the same result.
And the ‘~’ is just array concatenation. I like that it has it’s own operator for that, rather than re-using ‘+’ like in C++ strings.
Ah. From your example, though, it’s impossible to tell if the first ‘a’ is in the same spot on the stack as the second ‘a’, because the second ‘a’ is default-constructed when it is created, right? So it’s either a new stack slot or the first one reinitialized.
So ~ is array concat, but the right-hand operand doesn’t need to be an array. Fair enough, I guess.
The slice syntax for removing the last element of an array is both longer to type than pop_back() and has the bonus of reading completely differently and having unknown (to me) behavior when the array is empty. Array slices are cool, but that looks like abuse to me. Also, I can’t say I’m very happy with $. How many situations are there inside an indexer where you’ll use straight $, not ($ – 1)?
Yeah I had more text with an example that printed the addresses of the stack allocated values, but this post was way too long and that was cut. In this case you’ll just have to believe me that I tried several things and looked at the disassembly, and they are both being allocated on the stack. Just at different positions. Which is wasting a bit of stack space, but who cares about that when it prevents bugs?
The slice syntax is indeed longer than pop_back, but it makes sense to use that instead of defining a pop_back keyword. This just makes your array operations consistent across the language.
The array slice on an empty range behaves as you would expect: It throws a RangeException. (you can turn off bounds checking with a compiler flag)
The $ syntax is OK. It’s not the best solution, but it’s OK. You’d want to use straight $ if you want to do a pop_front: a = a[1 .. $]. (which btw is as efficient as pop_back in D)
Presumably, then, $ is usable in custom container classes? Even linked lists that don’t store a length, where length is an O(n) operation?
Ah, the range syntax is half-open… okay, that makes sense.
Can you disable bounds checking only for specific regions of code?